如何从基础类型的对象映射?

时间:2013-05-13 12:57:16

标签: automapper

我有两种类型:

class Source {
  public object Data { get; set; }
}

class Destination {
  public object Data { get; set; }
}

我希望在SourceDestination中的位置之间进行映射,属性Data是存在映射的某种类型。例如。

Mapper.CreateMap<SourceData1, DestinationData1>();
Mapper.CreateMap<SourceData2, DestinationData2>();

目前我的映射文件如下:

Mapper.CreateMap<Source, Destination>();
Mapper.CreateMap<SourceData1, DestinationData1>();
Mapper.CreateMap<SourceData2, DestinationData2>();

// This is the bit that looks bad to me:
Mapper
   .CreateMap<object, object>()
   .Include<SourceData1, DestinationData1>()
   .Include<SourceData2, DestinationData2>()

需要包含来自对象的所有允许的地图似乎有点笨拙,并且一旦解决方案有所增长,可能会变得非常烦人。如果我不包含它们,则Map无法正确映射这些类型。

有没有办法解决这个问题?

1 个答案:

答案 0 :(得分:0)

我意识到我希望从映射器中获取的行为是基于Source.Data的运行时类型将Destination.Data注入Source.Data。具体来说,对于给定类型TSource,我希望能够准确定义一个目标类型TDestination,这样当Source.Data具有类型TSource时,Destination.Data具有类型TDestination

MapDynamicMap的问题是您在执行映射时必须知道目标类型,但在配置时,您对Destination.Data的类型的所有了解都是它是object - 您需要显式声明类型目标类型,并且唯一可以做到这一点的方法是保持源类型到目标类型的映射。

我在AutoMapper配置方法上实现了几个扩展方法,允许注册Map进行注入,并使用注入策略解析Member:< / p>

using System;
using System.Collections.Generic;
using System.Linq.Expressions;

using AutoMapper;

/// <summary>
/// The <seealso cref="AutoMapper"/> extensions.
/// </summary>
public static class AutoMapperExtensions
{
    /// <summary>
    /// The injection maps.
    /// </summary>
    private static readonly SortedDictionary<Type, Type> Injections = 
        new SortedDictionary<Type, Type>(new TypeComparer());

    /// <summary>
    /// Registers a mapping expression for injection.
    /// </summary>
    /// <param name="expression">
    /// The expression to register fo injection.
    /// </param>
    /// <typeparam name="TSource">
    /// The source type.
    /// </typeparam>
    /// <typeparam name="TDestination">
    /// The destination type.
    /// </typeparam>
    /// <returns>
    /// The original <see cref="IMappingExpression"/> to allow for fluent chaining.
    /// </returns>
    /// <exception cref="InvalidOperationException">
    /// Thrown when <typeparam name="TSource"/> is an interface or has already been enabled.
    /// </exception>
    public static IMappingExpression<TSource, TDestination> RegisterForInjection<TSource, TDestination>(
        this IMappingExpression<TSource, TDestination> expression)
        where TSource : class
        where TDestination : class // These constraints mean that null can be mapped to null.
    {
        var sourceType = typeof(TSource);

        // Interfaces do not have a strict hierarchy so the cannot be registered.
        if (sourceType.IsInterface)
        {
            throw new InvalidOperationException(
                string.Format(
                "The type {0} is an interface and interface types cannot be registered for injection", 
                sourceType));
        }

        // This is important: for injection to work there can be exactly one target.
        if (Injections.ContainsKey(sourceType))
        {
            throw new InvalidOperationException(
                string.Format("The type {0} has already been registered for injection", sourceType));
        }

        Injections.Add(sourceType, typeof(TDestination));

        return expression;
    }

    /// <summary>
    /// Instructs the mapper to resolve the destination member using an injection 
    /// strategy based on the actual type of the source member at runtime.
    /// </summary>
    /// <param name="expression">
    /// The member expression on which to use an injection strategy.
    /// </param>
    /// <param name="sourceMember">
    /// The source member to map from.
    /// </param>
    /// <typeparam name="TSource">
    /// The source type.
    /// </typeparam>
    /// <typeparam name="TMember">
    /// The source member type (probably object).
    /// </typeparam>
    /// <exception cref="InvalidOperationException">
    /// When the actual type of <typeparam name="TMember"/> has not been registered for injection.
    /// </exception>
    public static void InjectFrom<TSource, TMember>(
        this IMemberConfigurationExpression<TSource> expression,
        Expression<Func<TSource, TMember>> sourceMember)
        where TMember : class
    {
        var getSourceMember = sourceMember.Compile();

        expression.ResolveUsing(
            s =>
            {
                var sourceMemberValue = getSourceMember(s);
                if (sourceMemberValue == null)
                {
                    return null;
                }

                var sourceMemberType = sourceMemberValue.GetType();

                Type destinationMemberType = null;

                // Because the injections are sorted by number of super classes it is 
                // guaranteed that the first one that is assignable from the source type
                // is the most derived.
                foreach (var injection in Injections)
                {
                    if (injection.Key.IsAssignableFrom(sourceMemberType))
                    {
                        // {injection.Key} value = default({sourceMemberType}) compiles.
                        destinationMemberType = injection.Value;
                        break;
                    }
                }

                if (destinationMemberType == null)
                {
                    throw new InvalidOperationException(
                        string.Format(
                        "The type {0} has not been enabled for injection.", 
                        sourceMemberType));
                }

                return Mapper.Map(sourceMemberValue, sourceMemberType, destinationMemberType);
            });
    }

    /// <summary>
    /// The type comparer.
    /// </summary>
    private class TypeComparer : IComparer<Type>
    {
        /// <summary>
        /// Compares <paramref name="x"/> with <paramref name="y"/>.
        /// </summary>
        /// <param name="x">
        /// The left hand type.
        /// </param>
        /// <param name="y">
        /// The right hand type
        /// </param>
        /// <returns>
        /// The difference in the number of super classes between y and x, specifically: 
        /// x &lt; y if x has more super classes than y
        /// </returns>
        public int Compare(Type x, Type y)
        {
            return CountSuperClasses(y) - CountSuperClasses(x);
        }

        /// <summary>
        /// Counts the number of super classes beneath <paramref name="type"/>.
        /// </summary>
        /// <param name="type">
        /// The type.
        /// </param>
        /// <returns>
        /// The number of super classes beneath <paramref name="type"/>.
        /// </returns>
        private static int CountSuperClasses(Type type)
        {
            var count = 0;
            while (type.BaseType != null)
            {
                ++count;
                type = type.BaseType;
            }

            return count;
        }
    }
}
相关问题