Merge two objects to produce third using AutoMapper

前端 未结 5 1518
日久生厌
日久生厌 2020-12-14 01:25

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

I\'ve started using AutoMapper and have a need to Map A -> B, and to add some properties from C so that B become

相关标签:
5条回答
  • 2020-12-14 01:52

    You can do this with the ValueInjecter

     a.InjectFrom(b)
      .InjectFrom(c)
      .InjectFrom<SomeOtherMappingAlgorithmDefinedByYou>(dOrBOrWhateverObject);
    
    0 讨论(0)
  • 2020-12-14 02:03

    Would this not work?

    var mappedB = _mapper.Map<A,B>(aInstance);
    _mapper.Map(instanceC,mappedB);
    
    0 讨论(0)
  • 2020-12-14 02:05

    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

    0 讨论(0)
  • 2020-12-14 02:06

    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.

    0 讨论(0)
  • 2020-12-14 02:06

    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 讨论(0)
提交回复
热议问题