Domanda

Ho la necessità di un oggetto per oggetto mapper nella mia applicazione. Ho provato un paio, ma non sono stati in grado di trovare tutto ciò che si adatta alle mie esigenze, così sto scrivendo la mia. Attualmente ho un'interfaccia come qui di seguito:

public interface IMapper<T, R> {
    T Map(R obj);
}

Ho quindi implementare un'AccountMapper che mappa un cliente a un account come:

public class AccountMapper : IMapper<Account, Customer> {
    Account Map(Customer obj) {
        // mapping code
    }
}

Questo funziona bene finora, ma ho diverse entità di origine che mappa alla stessa entità di destinazione. Per esempio ho un pagamento e una fattura che entrambi mappa per BillHistory. Per quanto sopra per sostenere questo, devo fare due mappatori separati (ad esempio. BillHistoryPaymentMapper e BillHistoryInvoiceMapper), che va bene. Tuttavia, mi piacerebbe essere in grado di implementare in modo leggermente diverso come qui di seguito. L'unico problema è che non so se è possibile e se sì, non so la sintassi corretta.

public interface IMapper<T> {
    T Map<R>(R obj);
}

public class BillHistoryMapper : IMapper<Account> {
    public BillHistory Map<Invoice>(Invoice obj) {
        // mapping code
    }
    public BillHistory Map<Payment>(Payment obj) {
        // mapping code
    }
}

Mentre la prima implementazione funziona bene, il secondo sarebbe leggermente più elegante. È questo possibile ed in caso affermativo quale sarebbe l'aspetto sintassi corretta come?

modifica -------

Non mi piace quando la gente fa questo, ma naturalmente ho dimenticato di citare un piccolo dettaglio. Abbiamo una classe astratta tra il mapper e l'interfaccia per implementare una logica comune tra tutti i mapper. Quindi la mia firma mapper è in realtà:

public class BillHistoryMapper : Mapper<BillHistory, Invoice> {
}

dove Mapper contiene:

public abstract class Mapper<T, R> : IMapper<T, R> {
    public IList<T> Map(IList<R> objList) {
        return objList.ToList<R>().ConvertAll<T>(new Converter<T, R>(Map));
    }
}
È stato utile?

Soluzione

Si dovrà utilizzare il prima interfaccia e implementare l'interfaccia più volte sul vostro oggetto:

public class BillHistoryMapper : IMapper<Account, Invoice>, 
                                 IMapper<Account, Payment> {     
   ...
}

I sarebbe seria considerazione dare un'occhiata a automapper invece di scrivere il proprio. Ci sono un sacco di sfumature nella mappatura che ha già risolto, per non parlare che è stato attraverso un sacco di test delle prestazioni, correzioni di bug, ecc.

Altri suggerimenti

Per quanto riguarda la classe astratta prendere in considerazione ottenere liberarsi di esso e la sua sostituzione con un metodo di estensione. Questo vi permetterà di utilizzare la funzione MapAll indipendentemente dal fatto che si implementa l'interfaccia o utilizzare una sorta di catena di ereditarietà.

public static class MapperExtensions
{
    public static IEnumerable<TOutput> MapAll<TInput, TOutput>
        (this IMapper<TInput, TOutput> mapper, IEnumerable<TInput> input)
    {
        return input.Select(x => mapper.Map(x));
    }
}

Questa sarà ora più facile quando si cerca di risolvere il problema di cui sopra, perché non hai più ereditare da una classe base è ora possibile implementare l'interfaccia di mappatura per i tipi che si desidera mappare.

public class BillHistoryMapper :
    IMapper<Invoice, BillHistory>, IMapper<Payment, BillHistory>
{
    public BillHistory Map<Invoice>(Invoice obj) {}
    public BillHistory Map<Payment>(Payment obj) {}
}

Considera anche cambiare la vostra IMapper parametri generici per essere il contrario (ho preso la libertà negli esempi precedenti):

public interface IMapper<in TInput, out TOutput>
{
    TOutput Map(TInput input);
}

La ragione di questo è che le mappe direttamente al System.Converter<T> delegare e si può fare qualcosa di simile:

IMapper<ObjectA, ObjectB> myAToBMapper = new MyAToBMapper();

ObjectA[] aArray = { new ObjectA(), new ObjectA() };
ObjectB[] bArray = Array.ConvertAll<ObjectA, ObjectB>(aArray, myAToBMapper.Map);

List<ObjectA> aList = new List<ObjectA> { new ObjectA(), new ObjectA() };
List<ObjectB> bList = aList.ConvertAll<ObjectB>(myAToBMapper.Map);

// Or

var aToBConverter = new Converter<ObjectA, ObjectB>(myAToBMapper.Map);
bArray = Array.ConvertAll(aArray, aToBConverter);
bList = aList.ConvertAll(aToBConverter);

automapper È stato anche suggerito che renderà la vita più facile. Tuttavia, se si voleva mantenere il vostro astrazione mappatura e avere il vostro codice di agnostico per la vostra strategia di mappatura, allora è molto facile da usare l'interfaccia di cui sopra per iniettare un wrapper automapper. Sarà anche significa che si può continuare a utilizzare il metodo di estensione MapAll spiegato in precedenza.

public class AutoMapperWrapper<in TInput, out TOutput> : IMapper<TInput, TOutput>
{
    public TOutput Map(TInput input)
    {
        return Mapper.Map<TOutput>(input);
    }
}

Ultima parola

Anche tenere a mente che non sta andando sempre per scoprire che la vostra strategia di mappatura funziona su tutta la linea in modo da non cercare di combattere il dominio e la forza per adattare la vostra strategia di mappatura. Un esempio particolare è che potrebbe essere necessario mappare da due elementi di input in uno. Si può ovviamente fare questa misura la vostra strategia, ma si possono trovare diventa disordinato. In questo particolare esempio considerarlo una fusione.

Il secondo esempio funzionerà con solo poche modifiche:

// you have to include the R type in the declaration of the Mapper interface
public interface IMapper<T, R> {
    T Map<R>(R obj);
}

// You have to specify both IMapper implementations in the declaration
public class BillHistoryMapper : IMapper<Account, Invoice>, IMapper<Account, Payment> {
    public BillHistory Map<Invoice>(Invoice obj) {
        // mapping code
    }
    public BillHistory Map<Payment>(Payment obj) {
        // mapping code
    }
}

Non sono sicuro se questo in realtà si guadagna nulla sopra il modello esistente, però.

Se c'è un numero limitato di tipi che si desidera mappare da, quindi vorrei usare il primo metodo di dichiarare i tipi di ingresso e di uscita nella definizione dell'interfaccia. Poi un mapper può implementare le interfacce per ogni tipo di ingresso che supporta, in modo che le BillHistoryMapper sarebbe stata dichiarata come:

public class BillHistoryMapper : IMapper<BillHistory, Invoice>, IMapper<BillHistory, Payment>
{
   ...
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top