2013-05-13 55 views
0

我有两种类型:如何从基本类型的对象映射?

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无法正确映射这些类型。

有什么办法可以解决这个问题吗?

+0

这取决于 - 是映射基于约定,例如它将始终是'SourceData1'映射到'DestinationData1' - 将涉及哪些实际类型名称?我会建议一些基于约定的映射解决方案,它会根据命名自动从一个程序集中注册类型 – Charleh 2013-05-13 13:18:42

+0

或者 - “DynamicMap”可以为你工作,因为如果你试图自动注册,你并不担心关于数据整形... http://stackoverflow.com/questions/2030227/automapper-convention – Charleh 2013-05-13 13:21:10

+0

@Charleh - 是在这种情况下,可以安全地假设从'SourceData1'唯一的映射将是'DestinationData1'在应用程序。 – briantyler 2013-05-13 13:43:54

回答

0

我意识到我想从映射器得到的行为是基于Source.Data的运行时类型将Source.Data注入到Destination.Data中。具体而言,对于给定类型TSource,我希望能够正好定义一个目标类型TDestination,以便当Source.Data具有类型TSourceDestination.Data的类型为TDestination

MapDynamicMap的问题是,你必须知道目标类型,当你执行映射,但在配置的时候你知道的Destination.Data类型是,它是object - 你需要明确指出的类型目的地类型,唯一的方法是通过维护源类型到目标类型的映射。

,我实现了几个对AutoMapper配置方法,使一个Map登记注射并为Member扩展方法,使用注射的战略来解决:

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