繁体   English   中英

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

[英]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>>'

有没有一种方法可以:

  • 创建未选中的“动态”查询或
  • 将dbset更改为所需的类型,应用表达式并将其返回为通用类型?

您可以使用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM