2016-12-29 100 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