开发者

Merge two objects to produce third using AutoMapper

开发者 https://www.devze.com 2022-12-23 11:12 出处:网络
I know it\'s AutoMapper and not AutoMerge(r), but... I\'ve started using AutoMapper and have a need t开发者_StackOverflow中文版o Map A -> B, and to add some properties from C so that B become a kind

I know it's AutoMapper and not AutoMerge(r), but...

I've started using AutoMapper and have a need t开发者_StackOverflow中文版o Map A -> B, and to add some properties from C so that B become a kind of flat composite of A + C.

Is this possible in AutoMapper of should I just use AutoMapper to do the heavy lifting then manually map on the extra properties?


Would this not work?

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


You can do this with the ValueInjecter

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


I searched hard and long on this question and ended up implementing an extension method that merge's objects together.

I reference the steps on my blog http://twistyvortek.blogspot.com and here's the code:


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

Usage includes method chaining so you can merge multiple objects into one.

What I would do is use automapper to map part of the properties from your various sources into the same class of DTOs, etc. and then use this extension method to merge them together.


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

Hope this helps someone.


From what I remember with AutoMapper you have to define your mappings as one input to one output (maybe this has changed since - haven't utilized it for many a month).

If this is the case, maybe your mapping should be of KeyValuePair<A,C> (or some sort of object composing both A & C) => B

This way you can have one defined input parameter mapping to your outputted object


There is a nice example of merging multiple sources into a destination using autoMapper, here in Owain Wraggs' EMC Consulting Blog.

EDIT: To guard against the old "dead-link" syndrome, the essence of the code in Owain's blog is below.

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

The resultant calling code is nice an succinct.

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

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号