首页 文章

将“where”表达式应用于通用dbset以过滤某些数据

提问于
浏览
0

我正在编写一个类似于角色的系统;用户只能看到他们有权访问的数据 . 这适用于用于表单填充,搜索,列表,报告等的数据 .

我计划通过在执行之前向EF查询添加“WHERE”子句来实现为添加请求添加过滤器的方式 .

如果不是因为我们使用泛型这一事实,这将是简单的 .

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>>'

有没有办法:

  • 创建未检查的"dynamic"查询或

  • 将dbset更改为所需类型,应用表达式并将其返回到泛型类型?

2 回答

  • 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();
    
  • 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; }
    }
    

相关问题