我正在编写一个类似于角色的系统;用户只能看到他们有权访问的数据。这适用于用于表单填充,搜索,列表,报告等的数据。
我计划实施此方法,通过添加" WHERE"来添加过滤器来获取请求。执行前的EF查询子句。
如果不是因为我们使用泛型,这很简单。
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>>'
有没有办法:
答案 0 :(得分:0)
您可以使用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();
答案 1 :(得分:0)
通过寻找不同的方式来查看此问题,您可能会得到更好的服务。由于这是一个安全过滤器,您可能会发现意外地没有实现IFoo,并且您没有按照自己的想法进行过滤。
话虽如此,你可以做到这一点,但它需要几个步骤。您可以手动构建表达式,但我更喜欢在代码中构建表达式,然后使用ReplaceVisitor修改当前类型的表达式。
首先,我创建一个类型为Expression<Func<IFoo,bool>>
的过滤表达式。这不是最终的过滤表达式,但它是兼容的,因为我们正在检查泛型类型是否实现了IFoo。
接下来,我使用Expression Visitor将每个对IFoo参数表达式的引用替换为TEntity参数表达式。结果表达式将是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; }
}