2016-12-29 96 views
1

在我的项目中,我有一个Linq可查询(实际上是一个EF6集合),我需要将其转换为数据传输对象的集合。我在整个项目中都使用了AutoMapper,特别是它能够执行类型投影,从而减少了Linq查询生成的SQL数量。当投影到可为空的枚举时,AutoMapper抛出异常

但是我的一个DTO类有一个小问题。关联的数据库列包含一个可以为空的字符串,我希望将其映射为可空的枚举。但在运行时抛出异常

在类型'System.String'和'System.Nullable`1 [AutomapperEnumTest.Method]'之间未定义强制运算符。

下面是一个完整的测试程序演示该问题:(见.Net Fiddle

using System; 
using System.Linq; 
using AutoMapper; 
using AutoMapper.QueryableExtensions; 

namespace AutomapperEnumTest 
{ 
    public class Program 
    { 
     public static void Main() 
     { 
      Configure(); 
      var src = new SourceType[] { new SourceType { Name = "Rob", MethodName = "AUTO" } }; 
      var results = src.AsQueryable() 
         .ProjectTo<DestType>(); 

      foreach(var item in results) 
      { 
       Console.WriteLine(String.Format("DestType Name={0} Method={1}", item.Name, item.Method)); 
      } 
      Console.WriteLine("Done"); 
     } 

     private static void Configure() 
     { 
      Mapper.Initialize(cfg => 
      { 
       cfg.CreateMap<string, Method?>().ProjectUsing(src => src == "MANUAL" ? Method.MANUAL : Method.AUTO); 
       cfg.CreateMap<SourceType, DestType>() 
        .ForMember(dest => dest.Method, opt => opt.MapFrom(src => src.MethodName)); 
      }); 
     } 
    } 

    public enum Method 
    { 
     MANUAL=0, 
     AUTO=1 
    } 

    public class DestType 
    { 
     public string Name {get; set; } 
     public Method? Method {get; set; } 
    } 

    public class SourceType 
    { 
     public string Name {get; set; } 
     public string MethodName {get; set; } 
    } 
} 

现在,如果我的属性更改从Method?Method正常工作(见.Net Fiddle此修改)。但我不想这样做,所以我的问题是如何通知Linq/AutoMapper它应该使用我的ProjectUsing作为可空的枚举?

非常感谢。

回答

1

在最新的AutoMapper v5.2.0中也是如此。

查看源代码后,我认为这是ExpressionBuilder类中的一个错误,因为一些未知的原因NullableExpressionBinderCustomProjectionExpressionBinder(和其他人)给予更高的优先级,所以基本上当你映射非可空类型可空,所有自定义映射将被忽略。

我建议你在他们的存储库上报告它。在此之前,我可以向您建议以下解决方法(黑客)。在项目中包含以下自定义类:

using System.Linq.Expressions; 
using System.Reflection; 
using AutoMapper; 
using AutoMapper.QueryableExtensions; 
using AutoMapper.QueryableExtensions.Impl; 

class NullableExpressionBinderEx : IExpressionBinder 
{ 
    public static void Install() 
    { 
     var bindersField = typeof(ExpressionBuilder).GetField("Binders", BindingFlags.NonPublic | BindingFlags.Static); 
     var binders = (IExpressionBinder[])bindersField.GetValue(null); 
     binders[0] = new NullableExpressionBinderEx(); 
    } 
    IExpressionBinder baseBinder = new NullableExpressionBinder(); 
    private NullableExpressionBinderEx() { } 
    public bool IsMatch(PropertyMap propertyMap, TypeMap propertyTypeMap, ExpressionResolutionResult result) 
    { 
     if (propertyTypeMap != null && propertyTypeMap.CustomProjection != null) 
      return false; 
     return baseBinder.IsMatch(propertyMap, propertyTypeMap, result); 
    } 
    public MemberAssignment Build(IConfigurationProvider configuration, PropertyMap propertyMap, TypeMap propertyTypeMap, ExpressionRequest request, ExpressionResolutionResult result, IDictionary<ExpressionRequest, int> typePairCount) 
    { 
     return baseBinder.Build(configuration, propertyMap, propertyTypeMap, request, result, typePairCount); 
    } 
} 

然后将下面的行添加到您的Configure方法:

NullableExpressionBinderEx.Install(); 

和问题应该得到解决。

+1

感谢您提供这样一个完整的答案。我测试过了,这确实解决了这个问题。 – Rob

1

您可以手动将MethodName映射到Method,除非我在您的问题中丢失了某些东西。

private static void Configure() 
{ 
    Mapper.Initialize(cfg => 
    { 
     cfg.CreateMap<SourceType, DestType>() 
      .ForMember(dest => dest.Method, opt => opt.MapFrom(src => 
       src.MethodName == "MANUAL" ? Method.MANUAL : Method.AUTO)); 
    }); 
} 
+0

谢谢@Win,你的解决方案在这个简单的例子中起作用。我没有想到要这样做,因为在实际的应用程序中,枚举在多个地方使用,所以我希望类型具有自己的映射配置(例如'ProjectUsing'配置)。你知道这是否可能,如果没有,是什么妨碍了? – Rob

+0

在我的第二个.Net Fiddle示例中,您会看到有一个从'String'到'Method'枚举的全局映射。这工作正常,但同样的技术似乎不适用于'System.Nullable '。 – Rob

+0

*** ProjectTo ***扩展方法'src.AsQueryable()。ProjectTo ()''是错误的。你的代码与'var results = src.Select(Mapper.Map )一起工作;' – Win