[英]Apply “where” expression to generic dbset to filter certain data
我正在编写一个具有类似于角色的系统; 用户只能看到他们有权访问的数据。 这适用于用于表格填充,搜索,列表,报告等的数据。
我计划通过在执行之前在EF查询中添加“ WHERE”子句为Get请求添加过滤器的方式来实现。
如果不是因为我们正在使用泛型,这将很简单。
Get函数曾经是这个
public class EntityFactory<TEntity, TDto> : IEntityFactory<TEntity, TDto> where TEntity : class
{
private readonly DBContext _context;
private readonly IMapper _mapper;
private DbSet<TEntity> _dbset;
public EntityFactory(DBContext context, IMapper mapper)
{
//...
}
public async Task<List<TDto>> GetAsync()
{
List<TEntity> d = await _dbset.AsNoTracking().ToListAsync();
return _mapper.Map<List<TDto>>(d);
}
}
我想做的是:
public async Task<List<TDto>> GetAsync()
{
//If the object implements a special interface
if (i.GetInterfaces().Contains(typeof(IFoo)))
{
//expression to filter the data on a linked table containing the user's Id.
Expression<Func<Bar, bool>> exp = x => x.Foos.Any(a => a.UserId == _user.UserId);
//add the expression to the dbSet
_dbSet = _dbSet.Where(exp);
}
//Execute the get
List<TEntity> d = await q.AsNoTracking().ToListAsync();
//return the converted objects
return _mapper.Map<List<TDto>>(d);
}
但这行不通! 我收到此编译器错误:
Argument 2: cannot convert from 'System.Linq.Expressions.Expression<System.Func<Bar, bool>>' to 'System.Linq.Expressions.Expression<System.Func<TEntity, int, bool>>'
有没有一种方法可以:
您可以使用Linq动态查询来应用where子句。
var query = _dbSet.AsQueryable();
//If the object implements a special interface
if (typeof(IFoo).IsAssignableFrom(typeof(TEntity)))
{
query = query.Where("UserId = @0", _userId);
}
List<TEntity> d = await query.AsNoTracking().ToListAsync();
找到解决问题的另一种方法可能会更好。 由于这是一个安全筛选器,因此您可能会意外地发现未实现IFoo,并且您以为自己没有进行筛选。
话虽如此,您可以执行此操作,但需要几个步骤。 您可以手动构建表达式,但是我更喜欢在代码中构建表达式,然后使用ReplaceVisitor修改当前类型的表达式。
首先,我创建一个类型为Expression<Func<IFoo,bool>>
的过滤表达式。 这不是最终的过滤表达式,但它是兼容的,因为我们正在检查泛型是否实现了IFoo。
接下来,我使用表达式访问者用TEntity参数表达式替换对IFoo参数表达式的每个引用。 结果表达式将是Expresssion<Func<TEntity,bool>>
,因此我将其Expresssion<Func<TEntity,bool>>
转换为该表达式,以便编译器满意。
最后,我将结果表达式传递给Where子句。
注意:这是一个非异步版本,但它演示了原理。
public class EntityFactory<TEntity, TDto> : IEntityFactory<TEntity, TDto> where TEntity : class
{
private readonly DbContext _context;
private readonly IMapper _mapper = new Mapper(new MapperConfiguration(v => { }));
private DbSet<TEntity> _dbSet;
private Type i = typeof(TEntity);
private Expression<Func<IFoo, bool>> getFilterExpression(int userId)
{
return (x => x.Foos.Any(a => a.UserId == userId));
}
private class ReplaceVisitor : ExpressionVisitor
{
readonly Expression _originalExpression;
readonly Expression _replacementExpression;
public ReplaceVisitor(Expression originalExpression, Expression replacementExpression)
{
_originalExpression = originalExpression;
_replacementExpression = replacementExpression;
}
public override Expression Visit(Expression node)
{
return _originalExpression == node ? _replacementExpression : base.Visit(node);
}
public static Expression VisitExpression(Expression node, Expression originalExpression, Expression replacementExpression)
{
return new ReplaceVisitor(originalExpression, replacementExpression).Visit(node);
}
}
public List<TDto> Get()
{
IQueryable<TEntity> query = _dbSet;
//If the object implements a special interface
if (i.GetInterfaces().Contains(typeof(IFoo)))
{
var userId = 7;
var baseFilterExpression = getFilterExpression(userId);
var filterExpression = (Expression<Func<TEntity, bool>>)ReplaceVisitor.VisitExpression(
baseFilterExpression,
baseFilterExpression.Parameters.First(),
Expression.Parameter(typeof(TEntity)));
//add the expression to the dbSet
query = query.Where(filterExpression);
}
List<TEntity> d = query.AsNoTracking().ToList();
return _mapper.Map<List<TDto>>(d);
}
}
public interface IEntityFactory<TEntity, TDTO> { }
public interface IFoo
{
List<FooItem> Foos { get; set; }
}
public class FooItem
{
public int UserId { get; set; }
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.