自动映射投影失败,具有可空属性

时间:2017-06-20 09:11:34

标签: c# automapper

Automapper版本:6.0.2

我有两个类InternalEntity作为我的数据源,PublicEntity作为API公开。我尝试使用InternalEntity方法通过PublicEntity投影访问UseAsDataSource

问题是内部bool属性映射到公共Nullable<bool>属性。

当我在投影中使用此属性时失败,例如在那上面调用OrderBy。有关详细信息,请参阅下面的示例。

我可以为bool设置投影规则吗? - &GT; bool不知何故?

以下是代码示例:

using AutoMapper;
using AutoMapper.QueryableExtensions;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Sample
{
    internal class Program
    {
        public class PublicEntity
        {
            public string PublicName { get; set; }
            public bool? Active { get; set; }
        }
        public class InternalEntity
        {
            public string InternalName { get; set; }
            public bool Active { get; set; }
        }

        private static IMapper mapper;

        private static void SetupMapping()
        {
            var mc = new MapperConfiguration(cfg =>
            {
                cfg.CreateMap<InternalEntity, PublicEntity>()
                    .ForMember(dest => dest.PublicName, opt => opt.MapFrom(src => src.InternalName));

                // setup bool? -> bool projection somehow
            });

            mc.AssertConfigurationIsValid();
            mapper = mc.CreateMapper();
        }

        private static void Main(string[] args)
        {
            SetupMapping();

            try
            {
                IQueryable<PublicEntity> resultable = DataSource()
                    .UseAsDataSource(mapper)
                    .For<PublicEntity>()
                    .OrderBy(x => x.Active);

                resultable.ToList()
                    .ForEach(x => Console.WriteLine(x.PublicName));
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            Console.ReadLine();
        }


        private static IQueryable<InternalEntity> DataSource()
        {
            return new List<InternalEntity>()
            {
                new InternalEntity(){ InternalName = "Name 1", Active = true},
                new InternalEntity(){ InternalName = "Name 3", Active = true},
                new InternalEntity(){ InternalName = "Name 2", Active = false},
            }.AsQueryable();
        }
    }
}

例外:

System.ArgumentException: Expression of type 'System.Linq.Expressions.Expression`1[System.Func`2[Sample.Program+InternalEntity,System.Boolean]]' cannot be used for parameter of type 'System.Linq.Expressions.Expression`1[System.Func`2[Sample.Program+InternalEntity,System.Nullable`1[System.Boolean]]]' of method 'System.Linq.IOrderedQueryable`1[Sample.Program+InternalEntity] OrderBy[InternalEntity,Nullable`1](System.Linq.IQueryable`1[Sample.Program+InternalEntity], System.Linq.Expressions.Expression`1[System.Func`2[Sample.Program+InternalEntity,System.Nullable`1[System.Boolean]]])'
   at System.Linq.Expressions.Expression.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arg, ParameterInfo pi)
   at System.Linq.Expressions.Expression.ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ReadOnlyCollection`1& arguments)
   at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments)
   at AutoMapper.Mappers.ExpressionMapper.MappingVisitor.GetConvertedMethodCall(MethodCallExpression node)
   at AutoMapper.Mappers.ExpressionMapper.MappingVisitor.VisitMethodCall(MethodCallExpression node)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at AutoMapper.QueryableExtensions.Impl.SourceInjectedQueryProvider`2.ConvertDestinationExpressionToSourceExpression(Expression expression)
   at AutoMapper.QueryableExtensions.Impl.SourceInjectedQueryProvider`2.Execute[TResult](Expression expression)
   at AutoMapper.QueryableExtensions.Impl.SourceSourceInjectedQuery`2.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Sample.Program.Main(String[] args) in xxxx\Program.cs:line 48

2 个答案:

答案 0 :(得分:3)

我自己找到了解决方案。一条或另一条线路做得很好:

.ForMember(dest => dest.Active, opt => opt.MapFrom(src => (bool?)src.Active))

.ForMember(dest => dest.Active, opt => opt.MapFrom(src => new Nullable<bool>(src.Active)))

这些行在语义上是等效的。

请注意,这些行会生成不同的表达式树,如果要解析表达式树,这些树可能会变得很重要。

答案 1 :(得分:2)

这个错误听起来合乎逻辑 - 你试图转换一个可以容纳&#34; true&#34;,&#34; false&#34;或者&#34;没有价值&#34;另一个只能持有&#34; true&#34;或&#34;假&#34;。

您可以告诉它从可空Value

进行映射
var mc = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<InternalEntity, PublicEntity>()
        .ForMember(dest => dest.PublicName, opt => opt.MapFrom(src => src.InternalName))
        .ForMember(dest => dest.Active, opt => opt.MapFrom(src => src.Active.Value));
});

请注意,如果源Active没有值(显然),它将抛出异常。

如果Active等于null表示&#34;无效&#34;,那么您只需使用??运算符:

.ForMember(dest => dest.Active, opt => opt.MapFrom(src => src.Active.Value ?? false));

但是为什么你需要一个可以为空的呢? :)