你怎么能更新的附加参数的LINQ表达式? [英] How can you update a Linq Expression with additional parameters?

查看:102
本文介绍了你怎么能更新的附加参数的LINQ表达式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有LINQ表达式,其可根据某些条件被改变。我愿做一个例子(左空白位,我不知道):

 表达式来; Func键<项目,布尔>>过滤器=(项目P)=> p.UserName ==鲍勃; 
如果(showArchived)
{
//更新过滤器,以增加和放大器;&安培; p.Archived
}
//查询当过滤器是内置
的IEnumerable&所述数据库;项目>项目= unitOfWork.ProjectRepository.Get(过滤器);



如何更新过滤器添加任何额外的参数?



在所有记录中检索一下,然后我用一个其中,来进一步过滤结果。然而这导致更多的数据库查询比是绝对必要的。



 的IEnumerable<项目>项目= unitOfWork.ProjectRepository.Get(过滤器); 
如果(showArchived)
{
项目= projects.Where(P => p.Archived);
}



Get方法是使用GenericRepository模式:

 公共类GenericRepository< TEntity>其中,TEntity:类
{
内部ProgrammeDBContext背景;
内部DbSet< TEntity> dbSet;

公共GenericRepository(ProgrammeDBContext上下文)
{
this.context =背景;
this.dbSet = context.Set< TEntity>();
}

公共虚拟IEnumerable的< TEntity>获取(
表达式来; Func键< TEntity,布尔>>过滤器= NULL,
Func键< IQueryable的< TEntity>中IOrderedQueryable< TEntity>>的OrderBy = NULL,
串includeProperties = )
{
&IQueryable的LT; TEntity>查询= dbSet;

如果(过滤器!= NULL)
{
=查询query.Where(过滤器);
}

的foreach(在includeProperties.Split
变种includeProperty(新的char [] {','},StringSplitOptions.RemoveEmptyEntries))
{
查询= query.Include(includeProperty);
}

如果(排序依据!= NULL)
{
返回排序依据(查询).ToList();
}
,否则
{
返回query.ToList();
}
}

公共虚拟TEntity GetByID(对象ID)
{
返回dbSet.Find(ID);
}

公共虚拟无效插入(TEntity实体)
{
dbSet.Add(实体);
}

公共虚拟无效删除(对象ID)
{
TEntity entityToDelete = dbSet.Find(ID);
删除(entityToDelete);
}

公共虚拟无效删除(TEntity entityToDelete)
{
如果(context.Entry(entityToDelete).STATE == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}

公共虚拟无效更新(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).STATE = EntityState.Modified;
}

公共虚拟IEnumerable的< TEntity> GetWithRawSql(查询字符串,params对象[]参数)
{
返回dbSet.SqlQuery(查询参数).ToList();
}
}



更新结果
创建基于下面的代码由Marc Gravell和大卫乙一些扩展方法,解决了我

 公共问题静态类LinqExtensionMethods 
{
公共静态表达式来; Func键< T,BOOL>> CombineOr< T>(PARAMS表达式来; Func键< T,BOOL>> []过滤器)
{
返回filters.CombineOr();
}

公共静态表达式来; Func键< T,BOOL>> CombineOr< T>(这个IEnumerable的<表达式来; Func键< T,BOOL>>>过滤器)
{
如果
{
表达式来(filters.Any()!) ; Func键< T,BOOL>> alwaysTrue = X =>真正;
返回alwaysTrue;
}
表达式来; Func键< T,BOOL>> firstFilter = filters.First();

VAR lastFilter = firstFilter;
表达式来; Func键< T,BOOL>>结果= NULL;
的foreach(在filters.Skip VAR nextFilter(1))
{
变种nextExpression =新ReplaceVisitor(lastFilter.Parameters [0],nextFilter.Parameters [0])。参观(lastFilter。身体);
结果= Expression.Lambda<&Func键LT; T,BOOL>>(Expression.OrElse(nextExpression,nextFilter.Body),nextFilter.Parameters);
lastFilter = nextFilter;
}
返回结果;
}

公共静态表达式来; Func键< T,BOOL>> CombineAnd< T>(PARAMS表达式来; Func键< T,BOOL>> []过滤器)
{
返回filters.CombineAnd();
}

公共静态表达式来; Func键< T,BOOL>> CombineAnd< T>(这个IEnumerable的<表达式来; Func键< T,BOOL>>>过滤器)
{
如果
{
表达式来(filters.Any()!) ; Func键< T,BOOL>> alwaysTrue = X =>真正;
返回alwaysTrue;
}
表达式来; Func键< T,BOOL>> firstFilter = filters.First();

VAR lastFilter = firstFilter;
表达式来; Func键< T,BOOL>>结果= NULL;
的foreach(在filters.Skip VAR nextFilter(1))
{
变种nextExpression =新ReplaceVisitor(lastFilter.Parameters [0],nextFilter.Parameters [0])。参观(lastFilter。身体);
结果= Expression.Lambda<&Func键LT; T,BOOL>>(Expression.AndAlso(nextExpression,nextFilter.Body),nextFilter.Parameters);
lastFilter = nextFilter;
}
返回结果;
}

类ReplaceVisitor:ExpressionVisitor
{
私人只读的,以表达;
公共ReplaceVisitor(从表达,表达于)
{
this.from =距离;
this.to =来;
}
公众覆盖式访问(Expression节点)
{
返回节点==而来?于:base.Visit(节点);
}
}
}


解决方案

如果我理解的问题,那么最有可能这里有一个问题:

 的IEnumerable<项目>项目= unitOfWork.ProjectRepository.Get(过滤器); 

项目的任何工作将是使用可枚举,而不是可查询;这大概应该是:



 的IQueryable<项目>项目= unitOfWork.ProjectRepository.Get(过滤器); 
如果(showArchived)
{
项目= projects.Where(P => p.Archived);
}



后者的组合的。凡应该工作,建立更为严格的查询的之前发送到服务器。



您的其他选择是重写过滤器在发送前合并:

 使用系统;使用System.Linq.Expressions 
;

静态类节目
{
静态无效的主要()
{
表达式来; Func键<富,布尔>>过滤器1 = X => x.A> 1;
表达式来; Func键<富,布尔>>过滤器2 = X => x.B> 2.5;

//合并两个谓词:
//需要改写lambda表达式之一,从另一
变种rewrittenBody1 =新ReplaceVisitor(
过滤器1中的参数交换.Parameters [0],filter2.Parameters [0])访问(filter1.Body)。
VAR newFilter = Expression.Lambda<&Func键LT;富,布尔>>(
Expression.AndAlso(rewrittenBody1,filter2.Body),filter2.Parameters);
// newFilter相当于:X => x.A> 1和;&放大器; x.B> 2.5
}
}
类Foo
{
公众诠释A {搞定;组; }
公众持股量的B {搞定;组; }
}
类ReplaceVisitor:ExpressionVisitor
{
私人只读的,以表达;
公共ReplaceVisitor(从表达,表达于)
{
this.from =距离;
this.to =来;
}
公众覆盖式访问(Expression节点)
{
返回节点==而来?于:base.Visit(节点);
}
}

或重新编写方式,让使用方便

 使用系统;使用System.Linq.Expressions 
;

静态类节目
{
静态无效的主要()
{
表达式来; Func键<富,布尔>>过滤= X => x.A> 1;

布尔applySecondFilter = TRUE;
如果(applySecondFilter)
{
滤波器=组合(过滤器,X => x.B> 2.5);
}
VAR数据= repo.Get(过滤器);
}
静态表达式来; Func键< T,BOOL>>结合< T>(表达式来; Func键< T,BOOL>>过滤器1,表达式来; Func键< T,BOOL>>过滤器2)
{
//合并两个谓词:
//需要重写lambda表达式之一,从另一
变种rewrittenBody1在参数交换=新ReplaceVisitor(
filter1.Parameters [0],filter2.Parameters [0])。访问(filter1.Body) ;
VAR newFilter = Expression.Lambda<&Func键LT; T,BOOL>>(
Expression.AndAlso(rewrittenBody1,filter2.Body),filter2.Parameters);
返回newFilter;
}
}
类Foo
{
公众诠释A {搞定;组; }
公众持股量的B {搞定;组; }
}
类ReplaceVisitor:ExpressionVisitor
{
私人只读的,以表达;
公共ReplaceVisitor(从表达,表达于)
{
this.from =距离;
this.to =来;
}
公众覆盖式访问(Expression节点)
{
返回节点==而来?于:base.Visit(节点);
}
}


I have a Linq Expression, which may be altered depending on certain conditions. An example of what I would like to do (left blank the bit I am not sure about):

Expression<Func<Project, bool>> filter = (Project p) => p.UserName == "Bob";
if(showArchived)
{
    // update filter to add && p.Archived
}
// query the database when the filter is built
IEnumerable<Project> projects = unitOfWork.ProjectRepository.Get(filter);

How do I update the filter to add any extra parameters?

At the moment all the records are retrieved, then I use a Where to further filter the results. However that results in more queries to the database than are strictly necessary.

IEnumerable<Project> projects = unitOfWork.ProjectRepository.Get(filter);
if(showArchived)
{
    projects = projects.Where(p => p.Archived);
}

Get method is using the GenericRepository pattern:

public class GenericRepository<TEntity> where TEntity : class
{
    internal ProgrammeDBContext context;
    internal DbSet<TEntity> dbSet;

    public GenericRepository(ProgrammeDBContext context)
    {
        this.context = context;
        this.dbSet = context.Set<TEntity>();
    }

    public virtual IEnumerable<TEntity> Get(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = "")
    {
        IQueryable<TEntity> query = dbSet;

        if (filter != null)
        {
            query = query.Where(filter);
        }

        foreach (var includeProperty in includeProperties.Split
            (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }

        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }
        else
        {
            return query.ToList();
        }
    }

    public virtual TEntity GetByID(object id)
    {
        return dbSet.Find(id);
    }

    public virtual void Insert(TEntity entity)
    {
        dbSet.Add(entity);
    }

    public virtual void Delete(object id)
    {
        TEntity entityToDelete = dbSet.Find(id);
        Delete(entityToDelete);
    }

    public virtual void Delete(TEntity entityToDelete)
    {
        if (context.Entry(entityToDelete).State == EntityState.Detached)
        {
            dbSet.Attach(entityToDelete);
        }
        dbSet.Remove(entityToDelete);
    }

    public virtual void Update(TEntity entityToUpdate)
    {
        dbSet.Attach(entityToUpdate);
        context.Entry(entityToUpdate).State = EntityState.Modified;
    }

    public virtual IEnumerable<TEntity> GetWithRawSql(string query, params object[] parameters)
    {
        return dbSet.SqlQuery(query, parameters).ToList();
    }
}

Update
Created some extension methods based on the code below by Marc Gravell and David B, solves the problem for me

public static class LinqExtensionMethods
{
    public static Expression<Func<T, bool>> CombineOr<T>(params Expression<Func<T, bool>>[] filters)
    {
        return filters.CombineOr();
    }

    public static Expression<Func<T, bool>> CombineOr<T>(this IEnumerable<Expression<Func<T, bool>>> filters)
    {
        if (!filters.Any())
        {
            Expression<Func<T, bool>> alwaysTrue = x => true;
            return alwaysTrue;
        }
        Expression<Func<T, bool>> firstFilter = filters.First();

        var lastFilter = firstFilter;
        Expression<Func<T, bool>> result = null;
        foreach (var nextFilter in filters.Skip(1))
        {
            var nextExpression = new ReplaceVisitor(lastFilter.Parameters[0], nextFilter.Parameters[0]).Visit(lastFilter.Body);
            result = Expression.Lambda<Func<T, bool>>(Expression.OrElse(nextExpression, nextFilter.Body), nextFilter.Parameters);
            lastFilter = nextFilter;
        }
        return result;
    }

    public static Expression<Func<T, bool>> CombineAnd<T>(params Expression<Func<T, bool>>[] filters)
    {
        return filters.CombineAnd();
    }

    public static Expression<Func<T, bool>> CombineAnd<T>(this IEnumerable<Expression<Func<T, bool>>> filters)
    {
        if (!filters.Any())
        {
            Expression<Func<T, bool>> alwaysTrue = x => true;
            return alwaysTrue;
        }
        Expression<Func<T, bool>> firstFilter = filters.First();

        var lastFilter = firstFilter;
        Expression<Func<T, bool>> result = null;
        foreach (var nextFilter in filters.Skip(1))
        {
            var nextExpression = new ReplaceVisitor(lastFilter.Parameters[0], nextFilter.Parameters[0]).Visit(lastFilter.Body);
            result = Expression.Lambda<Func<T, bool>>(Expression.AndAlso(nextExpression, nextFilter.Body), nextFilter.Parameters);
            lastFilter = nextFilter;
        }
        return result;
    }

    class ReplaceVisitor : ExpressionVisitor
    {
        private readonly Expression from, to;
        public ReplaceVisitor(Expression from, Expression to)
        {
            this.from = from;
            this.to = to;
        }
        public override Expression Visit(Expression node)
        {
            return node == from ? to : base.Visit(node);
        }
    }
}

解决方案

If I understand the question, then most likely here's the problem:

IEnumerable<Project> projects = unitOfWork.ProjectRepository.Get(filter);

Any work on projects is going to be using Enumerable, not Queryable; it should probably be:

IQueryable<Project> projects = unitOfWork.ProjectRepository.Get(filter);
if(showArchived)
{
    projects = projects.Where(p => p.Archived);
}

The latter is composable, and .Where should work as you expect, building up a more restrictive query before sending it to the server.

Your other option is to rewrite the filter to combine before sending:

using System;
using System.Linq.Expressions;

static class Program
{
    static void Main()
    {
        Expression<Func<Foo, bool>> filter1 = x => x.A > 1;
        Expression<Func<Foo, bool>> filter2 = x => x.B > 2.5;

        // combine two predicates:
        // need to rewrite one of the lambdas, swapping in the parameter from the other
        var rewrittenBody1 = new ReplaceVisitor(
            filter1.Parameters[0], filter2.Parameters[0]).Visit(filter1.Body);
        var newFilter = Expression.Lambda<Func<Foo, bool>>(
            Expression.AndAlso(rewrittenBody1, filter2.Body), filter2.Parameters);
        // newFilter is equivalent to: x => x.A > 1 && x.B > 2.5
    }
}
class Foo
{
    public int A { get; set; }
    public float B { get; set; }
}
class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

Or re-written in a way to allow convenient usage:

using System;
using System.Linq.Expressions;

static class Program
{
    static void Main()
    {
        Expression<Func<Foo, bool>> filter = x => x.A > 1;

        bool applySecondFilter = true;
        if(applySecondFilter)
        {
            filter = Combine(filter, x => x.B > 2.5);
        }
        var data = repo.Get(filter);
    }
    static Expression<Func<T,bool>> Combine<T>(Expression<Func<T,bool>> filter1, Expression<Func<T,bool>> filter2)
    {
        // combine two predicates:
        // need to rewrite one of the lambdas, swapping in the parameter from the other
        var rewrittenBody1 = new ReplaceVisitor(
            filter1.Parameters[0], filter2.Parameters[0]).Visit(filter1.Body);
        var newFilter = Expression.Lambda<Func<T, bool>>(
            Expression.AndAlso(rewrittenBody1, filter2.Body), filter2.Parameters);
        return newFilter;
    }
}
class Foo
{
    public int A { get; set; }
    public float B { get; set; }
}
class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

这篇关于你怎么能更新的附加参数的LINQ表达式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆