Pregunta

Tengo la necesidad de un objeto a objeto mapper en mi solicitud. He probado a cabo unos pocos, pero no he podido encontrar nada que se adapte a mis necesidades, por lo que estoy escribiendo mi propio. Actualmente tengo una interfaz como a continuación:

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

a continuación, poner en práctica un AccountMapper que se asigna un cliente a una cuenta como:

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

Esto funciona bien hasta el momento, sin embargo, tengo varias entidades de origen que se asignan a la misma entidad de destino. Por ejemplo tengo un pago y una factura que tanto mapa para BillHistory. Por lo anterior para apoyar esto, Necesito hacer dos mapeadores separadas (es decir. BillHistoryPaymentMapper y BillHistoryInvoiceMapper), que está bien. Sin embargo, me gustaría ser capaz de aplicar un poco diferente, como a continuación. El único problema es que no sé si es posible y si es así, no sé la sintaxis correcta.

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
    }
}

Mientras que la primera aplicación funciona bien, el segundo sería un poco más elegante. Es esto posible y si es así ¿cuál sería el aspecto sintaxis correcta como?

editar -------

No me gusta cuando la gente hace esto, pero por supuesto me olvidó mencionar un pequeño detalle. Tenemos una clase abstracta entre el asignador y la interfaz para implementar una lógica común a través de todos los creadores de mapas. Así que mi firma asignador es en realidad:

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

donde 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));
    }
}
¿Fue útil?

Solución

tendrá que usar su primera interfaz e implementar la interfaz varias veces en su objeto:

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

Me consideraría seria echar un vistazo a AutoMapper en lugar de escribir el suyo propio. Hay una gran cantidad de matices en la cartografía que ya ha resuelto, por no hablar de que ha sido a través de un montón de pruebas de rendimiento, corrección de errores, etc.

Otros consejos

Con respecto a su clase abstracta considere deshacerse de él y su sustitución por un método de extensión. Esto le permitirá utilizar la función MapAll independientemente de si se implementa la interfaz o utilizar algún tipo de cadena de herencia.

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));
    }
}

Esto ahora será más fácil cuando se trata de resolver el problema anterior porque ya no tiene que hereda de una clase base ahora se puede implementar la interfaz de mapeo para los tipos que desea asignar.

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

Ten en cuenta también el cambio de los parámetros genéricos de su IMapper ser al revés (he tomado la libertad en los ejemplos anteriores):

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

La razón de esto es que se asigna directamente a la System.Converter<T> delega y se puede hacer algo como:

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 También se ha sugerido que harán de su vida más fácil. Sin embargo, si quería mantener su abstracción mapeo y tener su agnóstico código a su estrategia de mapeo, entonces es muy fácil de usar interfaz anterior para inyectar una envoltura alrededor de AutoMapper. También significa que usted puede seguir utilizando el método de extensión MapAll explicó anteriormente.

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

palabra final

También hay que tener en cuenta que no siempre se va a encontrar que su estrategia de mapeo funcionará en todos los ámbitos, así que no trate de luchar contra su dominio y obligarlo a adaptarse a su estrategia de mapeo. Un ejemplo particular es que podría tener que asignar a partir de dos elementos de entrada en una sola. Es obvio que puede hacer que este se ajusta a su estrategia, pero puede que le convierte en desordenado. En este ejemplo particular considerarlo una fusión.

Su segundo ejemplo trabajará con sólo unos pocos cambios:

// 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
    }
}

No estoy seguro de si esto realmente se gana nada sobre el patrón existente, sin embargo.

Si hay un número limitado de tipos que desea asignar a partir, a continuación, me gustaría utilizar el primer método de declarar los tipos de entrada y salida en la definición de interfaz. A continuación, un asignador puede implementar interfaces para cada tipo de entrada que da soporte, por lo que su BillHistoryMapper sería declarado como:

public class BillHistoryMapper : IMapper<BillHistory, Invoice>, IMapper<BillHistory, Payment>
{
   ...
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top