2010-03-17 16 views
22

Sé que es AutoMapper y no AutoMerge (r), pero ...Combinar dos objetos para producir tercer usando AutoMapper

He empezado a utilizar AutoMapper y tienen una necesidad a Mapa A -> B y añadir algunas propiedades de C para que B se convierta en un tipo de compuesto plano de A + C.

¿Es esto posible en AutoMapper de si debo usar AutoMapper para hacer el trabajo pesado y luego hacer un mapa manual de las propiedades adicionales?

Respuesta

4

Por lo que recuerdo, con AutoMapper debe definir sus asignaciones como una entrada a una salida (puede que esto haya cambiado desde entonces, no la ha utilizado durante muchos meses).

Si este es el caso, tal vez su asignación debe ser de KeyValuePair<A,C> (o algún tipo de objeto que compone tanto A & C) => B

De esta manera usted puede tener una asignación de parámetros de entrada definido a su objeto emitida

+0

El objeto compuesto parece ser el camino a seguir, como la idea de usar una clase existente también - Lo intentaré. –

+0

Casi siempre hacemos un B -> BDto. Seguimos encontrando problemas con el nombramiento de colisiones para tratar de fusionar automáticamente las cosas. –

+0

Me gustaría obtener imágenes de la convención automática enfoque basado sería muy difícil de hacer (sobre todo con las colisiones), pero AutoMapper al menos a permitir a definir su propia función/delegado a utilizar para las asignaciones, por lo que al menos en este caso, uno puede hágalo manualmente ... y tampoco necesita mucho código: ¡listo para la buena utilidad! :) – saret

12

Sería esto no funciona?

var mappedB = _mapper.Map<A,B>(aInstance); 
_mapper.Map(instanceC,mappedB); 
+16

No lo sé, ¿verdad? –

14

Usted puede hacer esto con la ValueInjecter

a.InjectFrom(b) 
    .InjectFrom(c) 
    .InjectFrom<SomeOtherMappingAlgorithmDefinedByYou>(dOrBOrWhateverObject); 
+4

Esta herramienta es genial. – Merritt

+0

Utilizándolo ahora para mapear un montón de diferentes objetos tipo dto en una entidad EF. – Merritt

+1

@Omu, herramienta muy interesante, aunque no estoy seguro, si responde la pregunta anterior. – hgulyan

4

Busqué duro y largo plazo sobre esta cuestión y terminó la implementación de un método de extensión que se funden de objetos entre sí.

que hacen referencia a los pasos en mi blog http://twistyvortek.blogspot.com y aquí está el código:

 

    using System; 

    namespace Domain.Models 
    { 
     public static class ExtendedMethods 
     { 
      /// <summary> 
      /// Merges two object instances together. The primary instance will retain all non-Null values, and the second will merge all properties that map to null properties the primary 
      /// </summary> 
      /// <typeparam name="T">Type Parameter of the merging objects. Both objects must be of the same type.</typeparam> 
      /// <param name="primary">The object that is receiving merge data (modified)</param> 
      /// <param name="secondary">The object supplying the merging properties. (unmodified)</param> 
      /// <returns>The primary object (modified)</returns> 
      public static T MergeWith<T>(this T primary, T secondary) 
      { 
       foreach (var pi in typeof (T).GetProperties()) 
       { 
        var priValue = pi.GetGetMethod().Invoke(primary, null); 
        var secValue = pi.GetGetMethod().Invoke(secondary, null); 
        if (priValue == null || (pi.PropertyType.IsValueType && priValue == Activator.CreateInstance(pi.PropertyType))) 
        { 
         pi.GetSetMethod().Invoke(primary, new[] {secValue}); 
        } 
       } 
       return primary; 
      } 
     } 
    } 

uso incluye el método de encadenamiento para que pueda combinar varios objetos en una sola.

Lo que me gustaría hacer es el uso AutoMapper para asignar parte de las propiedades de sus diversas fuentes en la misma clase de dtos, etc., y luego usar este método de extensión para unirlos.

 

    var Obj1 = Mapper.Map(Instance1); 
    var Obj2 = Mapper.Map(Instance2); 
    var Obj3 = Mapper.Map(Instance3); 
    var Obj4 = Mapper.Map(Instance4); 

    var finalMerge = Obj1.MergeWith(Obj2) 
           .MergeWith(Obj3) 
           .MergeWith(Obj4); 

Espero que esto ayude a alguien.

+1

Estoy bastante seguro de que esto no se compilará. La clase que define un método de extensión debe ser no genérica y estática, entonces ¿cómo puede MergeWith (esta T primaria, T secundaria) ser válida? – SimonGates

+0

La clase no es genérica y estática. Es el método que es genérico, pero eso no es un problema. –

+3

public static T MergeWith (esta T primaria, T secundaria) –

3

hay un buen ejemplo de la fusión de varias fuentes en un destino utilizando AutoMapper, here en Owain Wraggs' EMC Consulting Blog.

EDITAR: Para protegerse contra el viejo "enlace-muerto" síndrome, la esencia del código en el blog de Owain está debajo.

/// <summary> 
/// Helper class to assist in mapping multiple entities to one single 
/// entity. 
/// </summary> 
/// <remarks> 
/// Code courtesy of Owain Wraggs' EMC Consulting Blog 
/// Ref: 
///  http://consultingblogs.emc.com/owainwragg/archive/2010/12/22/automapper-mapping-from-multiple-objects.aspx 
/// </remarks> 
public static class EntityMapper 
{ 
    /// <summary> 
    /// Maps the specified sources to the specified destination type. 
    /// </summary> 
    /// <typeparam name="T">The type of the destination</typeparam> 
    /// <param name="sources">The sources.</param> 
    /// <returns></returns> 
    /// <example> 
    /// Retrieve the person, address and comment entities 
    /// and map them on to a person view model entity. 
    /// 
    /// var personId = 23; 
    /// var person = _personTasks.GetPerson(personId); 
    /// var address = _personTasks.GetAddress(personId); 
    /// var comment = _personTasks.GetComment(personId); 
    /// 
    /// var personViewModel = EntityMapper.Map<PersonViewModel>(person, address, comment); 
    /// </example> 
    public static T Map<T>(params object[] sources) where T : class 
    { 
     // If there are no sources just return the destination object 
     if (!sources.Any()) 
     { 
      return default(T); 
     } 

     // Get the inital source and map it 
     var initialSource = sources[0]; 
     var mappingResult = Map<T>(initialSource); 

     // Now map the remaining source objects 
     if (sources.Count() > 1) 
     { 
      Map(mappingResult, sources.Skip(1).ToArray()); 
     } 

     // return the destination object 
     return mappingResult; 
    } 

    /// <summary> 
    /// Maps the specified sources to the specified destination. 
    /// </summary> 
    /// <param name="destination">The destination.</param> 
    /// <param name="sources">The sources.</param> 
    private static void Map(object destination, params object[] sources) 
    { 
     // If there are no sources just return the destination object 
     if (!sources.Any()) 
     { 
      return; 
     } 

     // Get the destination type 
     var destinationType = destination.GetType(); 

     // Itereate through all of the sources... 
     foreach (var source in sources) 
     { 
      // ... get the source type and map the source to the destination 
      var sourceType = source.GetType(); 
      Mapper.Map(source, destination, sourceType, destinationType); 
     } 
    } 

    /// <summary> 
    /// Maps the specified source to the destination. 
    /// </summary> 
    /// <typeparam name="T">type of teh destination</typeparam> 
    /// <param name="source">The source.</param> 
    /// <returns></returns> 
    private static T Map<T>(object source) where T : class 
    { 
     // Get thr source and destination types 
     var destinationType = typeof(T); 
     var sourceType = source.GetType(); 

     // Get the destination using AutoMapper's Map 
     var mappingResult = Mapper.Map(source, sourceType, destinationType); 

     // Return the destination 
     return mappingResult as T; 
    } 
} 

El código de llamada resultante es agradable y sucinto.

public ActionResult Index() 
    { 

     // Retrieve the person, address and comment entities and 
     // map them on to a person view model entity 
     var personId = 23; 

     var person = _personTasks.GetPerson(personId); 
     var address = _personTasks.GetAddress(personId); 
     var comment = _personTasks.GetComment(personId); 

     var personViewModel = EntityMapper.Map<PersonViewModel>(person, address, comment); 

     return this.View(personViewModel); 
    }