使用AutoMapper

时间:2019-09-13 08:22:35

标签: c# linq automapper

我有一个存储库->服务体系结构,该体系结构使用LINQ表达式来筛选许多数据。信息库与实体合作,服务与DTO合作。此刻,我使用Automapper将实体映射到dto,反之亦然。在存储库中,我有一个接受Expression> LINQ表达式的方法。服务使用Expression> LINQ表达式调用此方法。因此,我使用了Automapper将服务的表达式映射到存储库的表达式。 该项目成功构建,但是在运行时出现错误。

这是在服务中引发错误的方法:

public IQueryable<TDto> GetBy(Expression<Func<TDto, bool>> predicate)
            => this.Repository.GetBy(Mapper.Map<Expression<Func<TEntity, bool>>>(predicate))
                .ProjectTo<TDto>(Mapper.ConfigurationProvider);

这是存储库中调用的方法:

public IQueryable<TEntity> GetBy(Expression<Func<TEntity, bool>> predicate)
            => this.Context.Set<TEntity>().AsNoTracking().Where(predicate);

实体和dto之间的映射如下:

CreateMap<TEntity, TDto>();

CreateMap<TDto, TEntity>();

在运行时我收到此错误:

AutoMapper.AutoMapperMappingException: 'Missing type map configuration or unsupported mapping.'

此外,我尝试显式映射表达式:

CreateMap<Expression<Func<TEntity, bool>>, Expression<Func<TDto, bool>> >();

CreateMap<Expression<Func<TDto, bool>>, Expression<Func<TEntity, bool>>>();

但是我收到此错误:

System.InvalidOperationException: 'Code supposed to be unreachable'

有人可以解决吗?

1 个答案:

答案 0 :(得分:0)

我从未见过自动转换lambda的功能。 相反,您应该尝试使用Project方法。

例如:

var predicate = new Func<Dto, bool>(d => d.Id == 2);
var query = mapper.ProjectTo<Dto>(entities, null).Where(predicate);

查询将等同于:

var query = entities
    .Select(e => new Dto { Id = e.Id, [...] }) // mapping created using map registered in AutoMapper
    .Where(d => d.Id == 2)

您可以做的另一件事是自己映射表达式。作为星点,您可以使用Project方法产生的查询:

var query = mapper.ProjectTo<Dto>(entities, null);
var lambda = (LambdaExpression)((UnaryExpression)((MethodCallExpression) query.Expression).Arguments[1]).Operand;
var body = (MemberInitExpression)lambda.Body;
var bindings = body.Bindings;

由于上面的代码,您将获得诸如dto.Id = entity.Id之类的绑定数组。 有了它,编写自定义映射器应该很容易:

public static class MapperExtensions
{
    public static Expression<Func<TEntity, bool>> ConvertPredicate<TDto, TEntity>(this Mapper mapper, Expression<Func<TDto, bool>> predicate)
    {
        return (Expression<Func<TEntity, bool>>)new PredicateVisitor<TDto, TEntity>(mapper).Visit(predicate);
    }

    public class PredicateVisitor<TDto, TEntity> : ExpressionVisitor
    {
        private readonly ParameterExpression _entityParameter;
        private readonly MemberAssignment[] _bindings;

        public PredicateVisitor(Mapper mapper)
        {
            IQueryable<TDto> mockQuery = mapper.ProjectTo<TDto>(new TEntity[0].AsQueryable(), null);
            LambdaExpression lambdaExpression = (LambdaExpression)((UnaryExpression)((MethodCallExpression) mockQuery.Expression).Arguments[1]).Operand;

            this._bindings = ((MemberInitExpression)lambdaExpression.Body).Bindings.Cast<MemberAssignment>().ToArray();
            this._entityParameter = Expression.Parameter(typeof(TEntity));
        }

        // This is required to modify type parameters
        protected override Expression VisitLambda<T>(Expression<T> node)
        {
            return Expression.Lambda(
                base.Visit(node.Body),
                node.Parameters.Select(p => (ParameterExpression)base.Visit(p)).ToArray()
            );
        }

        // Do member mapping
        protected override Expression VisitMember(MemberExpression node)
        {
            MemberInfo member = node.Member;
            MemberAssignment binding = this._bindings.FirstOrDefault(b => b.Member == member);

            if (binding != null)
            {
                return base.Visit(binding.Expression);
            }

            return base.VisitMember(node);
        }

        // Replace parameters reference
        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (node.Type == typeof(TDto))
            {
                return this._entityParameter;
            }
            if (node.Type == typeof(TEntity))
            {
                return this._entityParameter;
            }

            return base.VisitParameter(node);
        }
    }
}