System.InvalidOperationException: 'Code supposed to be unreachable'

System.InvalidOperationException: 'Code supposed to be unreachable'

在与 AutoMapper 团队讨论后,他们建议我在这里提出问题。

简而言之,如果我使用

映射我需要的表达式
MapExpression<T>(source)

它工作得很好(考虑使用 AutoMapper.Extensions.ExpressionMapping 框架)。

顺便说一句,作者告诉我,即使我尝试使用

进行映射
Map(object, sourceType, targetType) 

它应该可以正常工作,但是当我使用这个方法时,出现了这个 post.

标题中描述的错误

为了提供帮助,我写了一个完整的示例来说明如何重现问题,如下所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

using AutoMapper;
using AutoMapper.Extensions.ExpressionMapping;

namespace AutoMapperExpressionMappingTest
{
    public class PresentationModelPerson
    {
        public string Name { get; set; }
        public DateTime Birthday { get; set; }
    }

    public class ApplicationModelPerson
    {
        public string Name { get; set; }
        public DateTime Birthday { get; set; }
    }

    public class DomainModelPerson
    {
        public string Name { get; set; }
        public DateTime Birthday { get; set; }
    }

    public class PresentationPerson
    {
        private readonly IMapper _mapper;
        public PresentationPerson(IMapper mapper) => _mapper = mapper;

        public  IEnumerable<PresentationModelPerson> List(int take, int skip,
            Expression<Func<IQueryable<PresentationModelPerson>, IOrderedQueryable<PresentationModelPerson>>> orderBy,
            Expression<Func<PresentationModelPerson, bool>> where, IList<Expression<Func<PresentationModelPerson, object>>> includes)
        {
            var applicationTake = take;
            var applicationSkip = skip;

            /*
             * if I map this way the mapping on domain class will fail with the following error:
             * System.InvalidOperationException: 'Code supposed to be unreachable'
             * (there's a reason on my project to use this way)
             */
            dynamic applicationOrderByObject = _mapper.Map(orderBy,
                typeof(Expression<Func<IQueryable<PresentationModelPerson>, IOrderedQueryable<PresentationModelPerson>>>
                ),
                typeof(Expression<Func<IQueryable<ApplicationModelPerson>, IOrderedQueryable<ApplicationModelPerson>>
                >));

            /*
             // if I map this way, it works perfectly //
            var applicationOrderBy =
                _mapper
                    .MapExpression<
                        Expression<Func<IQueryable<ApplicationModelPerson>, IOrderedQueryable<ApplicationModelPerson>>>
                    >(orderBy);
            */

            var applicationWhere = _mapper.MapExpression<Expression<Func<ApplicationModelPerson, bool>>>(where);

            var applicationInclude =
                _mapper.MapExpressionList<Expression<Func<ApplicationModelPerson, object>>>(includes).ToList();


            var applicationPerson = new ApplicationPerson(_mapper);

            applicationPerson.List(applicationTake, applicationSkip, applicationOrderByObject, applicationWhere,
                applicationInclude);

            throw new NotImplementedException();
        }
    }

    public class ApplicationPerson
    {
        private readonly IMapper _mapper;
        public ApplicationPerson(IMapper mapper) => _mapper = mapper;

        public IEnumerable<ApplicationModelPerson> List(int take, int skip,
            Expression<Func<IQueryable<ApplicationModelPerson>, IOrderedQueryable<ApplicationModelPerson>>> orderBy,
            Expression<Func<ApplicationModelPerson, bool>> where, IList<Expression<Func<ApplicationModelPerson, object>>> includes)
        {
            var domainTake = take;
            var domainSkip = skip;

            // this mapping will fail whatever I use this way or _mapper.Map(object, sourceType, targetType) //
            var domainOrderBy =
                _mapper
                    .MapExpression<
                        Expression<Func<IQueryable<DomainModelPerson>, IOrderedQueryable<DomainModelPerson>>>
                    >(orderBy);

            var domainWhere = _mapper.MapExpression<Expression<Func<DomainModelPerson, bool>>>(where);

            var domainInclude =
                _mapper.MapExpressionList<Expression<Func<DomainModelPerson, object>>>(includes).ToList();


            var domainPerson = new DomainPerson(_mapper);

            domainPerson.List(domainTake, domainSkip, domainOrderBy, domainWhere,
                domainInclude);

            throw new NotImplementedException();
        }
    }

    public class DomainPerson
    {
        private readonly IMapper _mapper;
        public DomainPerson(IMapper mapper) => _mapper = mapper;

        public IEnumerable<DomainModelPerson> List(int take, int skip,
            Expression<Func<IQueryable<DomainModelPerson>, IOrderedQueryable<DomainModelPerson>>> orderBy,
            Expression<Func<DomainModelPerson, bool>> where, IList<Expression<Func<DomainModelPerson, object>>> includes)
        {
            throw new NotImplementedException();
        }
    }

    public class ModelProfile : Profile
    {
        public ModelProfile()
        {
            CreateMap<PresentationModelPerson, ApplicationModelPerson>().ReverseMap();
            CreateMap<ApplicationModelPerson, DomainModelPerson>().ReverseMap();
        }
    }

    public class ExpressionProfile : Profile
    {
        public ExpressionProfile()
        {
            CreateMap<Expression<Func<PresentationModelPerson, bool>>,
                Expression<Func<ApplicationModelPerson, bool>>>().ReverseMap();

            CreateMap<Expression<Func<IQueryable<PresentationModelPerson>,
                    IOrderedQueryable<PresentationModelPerson>>>,
                Expression<Func<IQueryable<ApplicationModelPerson>, IOrderedQueryable<ApplicationModelPerson>>>>().ReverseMap();

            CreateMap<IList<Expression<Func<PresentationModelPerson, object>>>,
                IList<Expression<Func<ApplicationModelPerson, object>>>>().ReverseMap();


            CreateMap<Expression<Func<ApplicationModelPerson, bool>>,
                Expression<Func<DomainModelPerson, bool>>>().ReverseMap();

            CreateMap<Expression<Func<IQueryable<ApplicationModelPerson>,
                    IOrderedQueryable<ApplicationModelPerson>>>,
                Expression<Func<IQueryable<DomainModelPerson>, IOrderedQueryable<DomainModelPerson>>>>().ReverseMap();

            CreateMap<IList<Expression<Func<ApplicationModelPerson, object>>>,
                IList<Expression<Func<DomainModelPerson, object>>>>().ReverseMap();
        }
    }

    public class Container
    {
        public IMapper Mapper { get; }

        public Container()
        {
            var mapperConfiguration = new MapperConfiguration(
                configuration =>
                {
                    configuration.AddExpressionMapping();
                    configuration.AddProfile<ModelProfile>();
                    configuration.AddProfile<ExpressionProfile>();
                    configuration.AllowNullCollections = true;
                });

            Mapper = mapperConfiguration.CreateMapper();
            Mapper.ConfigurationProvider.AssertConfigurationIsValid();
        }
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            var mapper = new Container().Mapper;

            var presentationPerson = new PresentationPerson(mapper);

            Expression<Func<IQueryable<PresentationModelPerson>, IOrderedQueryable<PresentationModelPerson>>> orderBy = persons =>
                persons.OrderByDescending(person => person.Birthday);

            Expression<Func<PresentationModelPerson, bool>> where = person => !string.IsNullOrEmpty(person.Name);

            presentationPerson.List(1, 100, orderBy, where,
                new List<Expression<Func<PresentationModelPerson, object>>>());
        }
    }
}

这里有什么我遗漏的吗?及时:.NET Core 2.2、AutoMapper 9.0、AutoMapper.Extensions.ExpressionMapping 3.0.1,仅此而已。

谢谢。

作者回答并确认这是一个错误,所以我在他们的 Github 页面上发布了他们给我的解决方法。

这是问题的 link:https://github.com/AutoMapper/AutoMapper.Extensions.ExpressionMapping/issues/40

我们需要为它创建一个助手,像这样:

public static class ExpressionMappingHelper
{
    public static LambdaExpression MapExpression(this IMapper mapper, LambdaExpression expression, Type sourceExpressionType, Type destExpressionType)
    {
        if (expression == null)
            return default;

        //This calls public static TDestDelegate MapExpression<TSourceDelegate, TDestDelegate>(this IMapper mapper, TSourceDelegate expression)
        //in AutoMapper.Extensions.ExpressionMapping.MapperExtensions
        return (LambdaExpression)"MapExpression".GetMapExpressionMethod().MakeGenericMethod
        (
            sourceExpressionType,
            destExpressionType
        ).Invoke(null, new object[] { mapper, expression });
    }

    private static MethodInfo GetMapExpressionMethod(this string methodName)
        => typeof(AutoMapper.Extensions.ExpressionMapping.MapperExtensions).GetMethods().Single(m => m.Name == methodName && m.GetGenericArguments().Length == 2);
}

然后像这样调用扩展方法:

        dynamic applicationOrderByObject = _mapper.MapExpression(orderBy,
            typeof(Expression<Func<IQueryable<PresentationModelPerson>, IOrderedQueryable<PresentationModelPerson>>>
            ),
            typeof(Expression<Func<IQueryable<ApplicationModelPerson>, IOrderedQueryable<ApplicationModelPerson>>
            >));

在 AutoMapper 团队修复它之前,这将是解决方法。

希望这能像帮助我一样帮助别人。