【问题标题】:AutoMapper throws exception when projecting to nullable enum投影到可为空的枚举时 AutoMapper 抛出异常
【发布时间】:2016-12-29 18:28:28
【问题描述】:

在我的项目中,我有一个可查询的 Linq(实际上是一个 EF6 集合),我需要将其转换为数据传输对象的集合。我在整个项目中使用AutoMapper,尤其是因为它能够进行类型投影,从而减少了 Linq 查询生成的 SQL 数量。

但是我的一个 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 作为可空枚举?

非常感谢。

【问题讨论】:

    标签: c# linq exception automapper


    【解决方案1】:

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

    查看源代码后,我认为这是ExpressionBuilder 类中的一个错误,因为由于某些未知原因,NullableExpressionBinder 的优先级高于CustomProjectionExpressionBinder(和其他),所以基本上当您映射不可为空时type 为 nullable,所有自定义映射都将被忽略。

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

    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();
    

    问题应该解决了。

    【讨论】:

    • 感谢您提供如此完整的答案。我已经测试过了,这确实解决了问题。
    【解决方案2】:

    您可以手动将 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));
        });
    }
    

    【讨论】:

    • 谢谢@Win,您的解决方案在这个简单的示例中确实有效。我没想过尝试这样做,因为在实际应用程序中,枚举在多个地方使用,所以我希望该类型有自己的映射配置(例如 ProjectUsing 配置)。你知道这是否可行,如果不可行,有什么阻碍?
    • 在我的第二个 .Net Fiddle 示例中,您将看到从 StringMethod 枚举的全局映射。这很好用,但同样的技术似乎不适用于System.Nullable&lt;Method&gt;
    • ProjectTo 扩展方法src.AsQueryable().ProjectTo&lt;DestType&gt;() 有问题。您的代码适用于var results = src.Select(Mapper.Map&lt;SourceType, DestType&gt;);
    猜你喜欢
    • 2013-04-11
    • 2018-01-27
    • 2011-12-07
    • 1970-01-01
    • 1970-01-01
    • 2022-07-29
    • 1970-01-01
    • 2020-12-27
    相关资源
    最近更新 更多