我有一个存储库->服务体系结构,该体系结构使用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'
有人可以解决吗?
答案 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);
}
}
}