2010-05-13 7 views
11

Necesito un asignador de objetos para objeto en mi aplicación. He probado algunos, pero no he podido encontrar nada que se ajuste a mis necesidades, así que estoy escribiendo el mío. Actualmente tengo una interfaz como a continuación:Objeto basado en genéricos de C# para la consulta del asignador de objetos

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

entonces implementar 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 se asignan a BillHistory. Para que lo anterior lo soporte, necesito hacer dos mapeadores por separado (es decir, BillHistoryPaymentMapper y BillHistoryInvoiceMapper), lo cual está bien. Sin embargo, me encantaría poder implementarlo de forma ligeramente diferente, como a continuación. El único problema es que no sé si es posible y, de ser 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 
    } 
} 

Si bien la primera implementación funciona bien, la segunda sería un poco más elegante. ¿Es esto posible y, en caso afirmativo, cómo sería la sintaxis correcta?

edición -------

No me gusta cuando la gente hace esto, pero por supuesto se me olvidó mencionar un pequeño detalle. Tenemos una clase abstracta entre el mapeador y la interfaz para implementar una lógica común en todos los mapeadores. 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)); 
    } 
} 

Respuesta

3

Vas a tener que usar su primera interfaz e implementar la interfaz varias veces en su objeto:

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

Consideraría seriamente echar un vistazo a AutoMapper en lugar de escribir el suyo. Hay muchos matices en el mapeo que ya se han resuelto, sin mencionar que ha sido sometido a muchas pruebas de rendimiento, corrección de errores, etc.

+0

ver texto de arriba, el código no se muestra correctamente en el código comentario – Brian

+1

poner en práctica el método de mapa como un método de extensión en la interfaz IMapper. De esta forma, no tiene que usar una clase base abstracta. –

+0

He marcado esto como la respuesta b/c que respondió a mi pregunta original. También pude lograr esto sin una clase abstracta usando la sugerencia de Eric. ¡¡¡Gracias!!! – Brian

0

Su segundo ejemplo funcionará 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 si esto de hecho, te gana algo sobre el patrón existente.

0

Si hay un número limitado de tipos que desea asignar, entonces usaría el primer método para declarar los tipos de entrada y salida en la definición de la 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> 
{ 
    ... 
} 
1

Con respecto a su clase abstracta, considere deshacerse de ella y reemplazarla con un método de extensión. Esto le permitirá usar la función MapAll independientemente de si implementa la interfaz o utiliza 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 heredar 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) {} 
} 

Considera también cambiar sus IMapper parámetros genéricos que 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> delegado 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 tienen su código agnóstico 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 es posible que deba mapear desde dos elementos de entrada en uno. Obviamente puede hacer que esto se adapte a su estrategia, pero puede encontrar que se vuelve desordenado. En este ejemplo particular, considéralo una combinación.

Cuestiones relacionadas