使用 AutoMapper 映射表达式<Func<Dto, bool>> to Expression<Func<Entity, bool>>



我有一个存储库>服务体系结构,它使用 LINQ 表达式来筛选许多数据。存储库与实体合作,服务与 DTO 合作。目前,我使用自动映射器将实体映射到dto,反之亦然。在存储库中,我有一个接受表达式的方法> LINQ 表达式。此方法由服务使用表达式> 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'

有人有可能的解决方案吗?

我从未见过自动转换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);
}
}
}

最新更新