C# 如何使用附加参数更新Linq表达式?

C# 如何使用附加参数更新Linq表达式?,c#,linq,C#,Linq,我有一个Linq表达式,它可能会根据某些条件而改变。我想做的一个例子(我不确定的地方留空): Get方法正在使用GenericRepository模式: public class GenericRepository<TEntity> where TEntity : class { internal ProgrammeDBContext context; internal DbSet<TEntity> dbSet; public GenericRe

我有一个Linq表达式,它可能会根据某些条件而改变。我想做的一个例子(我不确定的地方留空):

Get方法正在使用GenericRepository模式:

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();
    }
}
公共类通用存储,其中tenty:class
{
内部编程b上下文上下文;
内部数据库集;
公共通用存储库(ProgrammeDBContext上下文)
{
this.context=上下文;
this.dbSet=context.Set();
}
公共虚拟IEnumerable Get(
表达式筛选器=空,
Func orderBy=null,
字符串includeProperties=“”)
{
IQueryable query=dbSet;
if(过滤器!=null)
{
query=query.Where(过滤器);
}
foreach(includeProperty.Split中的var includeProperty
(新字符[]{',},StringSplitOptions.RemoveEmptyEntries)
{
query=query.Include(includeProperty);
}
if(orderBy!=null)
{
returnorderby(query.ToList();
}
其他的
{
返回query.ToList();
}
}
公共虚拟实体GetByID(对象id)
{
返回dbSet.Find(id);
}
公共虚拟空白插入(TEntity实体)
{
添加(实体);
}
公共虚拟无效删除(对象id)
{
TEntity entityToDelete=dbSet.Find(id);
删除(entityToDelete);
}
公共虚拟无效删除(TEntity entityToDelete)
{
if(context.Entry(entityToDelete.State==EntityState.Detached)
{
数据库集连接(entityToDelete);
}
dbSet.Remove(entityToDelete);
}
公共虚拟无效更新(TEntity entityToUpdate)
{
数据库集附加(实体更新);
context.Entry(entityToUpdate.State=EntityState.Modified;
}
公共虚拟IEnumerable GetWithRawSql(字符串查询,参数对象[]参数)
{
返回dbSet.SqlQuery(查询,参数).ToList();
}
}
更新
Marc Gravell和David B基于下面的代码创建了一些扩展方法,为我解决了这个问题

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);
        }
    }
}
公共静态类LinqExtensionMethods
{
公共静态表达式组合(参数表达式[]筛选器)
{
返回过滤器.CombineOr();
}
公共静态表达式组合(此IEnumerable筛选器)
{
如果(!filters.Any())
{
表达式alwaysTrue=x=>true;
始终返回;
}
表达式firstFilter=filters.First();
var lastFilter=firstFilter;
表达式结果=null;
foreach(过滤器中的var nextFilter.Skip(1))
{
var nextExpression=new ReplaceVisitor(lastFilter.Parameters[0],nextFilter.Parameters[0])。访问(lastFilter.Body);
结果=Expression.Lambda(Expression.OrElse(nextExpression,nextFilter.Body),nextFilter.Parameters);
lastFilter=nextFilter;
}
返回结果;
}
公共静态表达式组合AND(参数表达式[]筛选器)
{
返回过滤器.CombineAnd();
}
公共静态表达式组合AND(此IEnumerable筛选器)
{
如果(!filters.Any())
{
表达式alwaysTrue=x=>true;
始终返回;
}
表达式firstFilter=filters.First();
var lastFilter=firstFilter;
表达式结果=null;
foreach(过滤器中的var nextFilter.Skip(1))
{
var nextExpression=new ReplaceVisitor(lastFilter.Parameters[0],nextFilter.Parameters[0])。访问(lastFilter.Body);
结果=Expression.Lambda(Expression.AndAlso(nextExpression,nextFilter.Body),nextFilter.Parameters);
lastFilter=nextFilter;
}
返回结果;
}
类替换访问者:ExpressionVisitor
{
私有只读表达式from、to;
公共访问者(表达式from、表达式to)
{
this.from=from;
这个;
}
公共重写表达式访问(表达式节点)
{
返回节点==从?到:基本访问(节点);
}
}
}

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

IEnumerable<Project> projects = unitOfWork.ProjectRepository.Get(filter);
后者是可组合的,
。其中
应该按照您的预期工作,在将其发送到服务器之前建立一个更严格的查询

您的另一个选择是在发送之前重写过滤器以合并:

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);
    }
}
使用系统;
使用System.Linq.Expressions;
静态类程序
{
静态void Main()
{
表达式过滤器1=x=>x.A>1;
表达式过滤器2=x=>x.B>2.5;
//组合两个谓词:
//需要重写其中一个lambda,从另一个替换参数
var rewritenbody1=新的替换访问者(
filter1.Parameters[0],filter2.Parameters[0])。访问(filter1.Body);
var newFilter=Expression.Lambda(
表达式AndAlso(重写Body1,filter2.Body),filter2.Parameters);
//newFilter相当于:x=>x.A>1&&x.B>2.5
}
}
福班
{
公共int A{get;set;}
公共浮点B{get;set;}
}
类替换访问者:ExpressionVisitor
{
私有只读表达式from、to;
公共访问者(表达式from、表达式to)
{
this.from=from;
这个;
}
公共重写表达式访问(表达式节点)
{
返回节点==从?到:基本访问(节点);
}
}
或以便于使用的方式重新编写:

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);
    }
}
使用系统;
使用System.Linq.Expressions;
静态类程序
{
静态void Main()
{
表达式过滤器=x=>x.A>1;
bool applySecondFilter=true;
if(applySecondFilter)
{
过滤器=联合收割机(过滤器,x=>
IQueryable<Project> projects = unitOfWork.ProjectRepository.Get(filter);
if(showArchived)
{
    projects = projects.Where(p => p.Archived);
}
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);
    }
}
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);
    }
}
Expression<Func<Project, bool>> filter = (Project p) => p.UserName == "Bob";
if(showArchived) {
     filter = (Project p) => p.UserName == "Bob" && p.Archived;
}
IEnumerable<Project> projects = unitOfWork.ProjectRepository.Get(filter);
var myFilters = new List<Expression<Func<Customer, bool>>>();
myFilters.Add(c => c.Name.StartsWith("B"));
myFilters.Add(c => c.Orders.Count() == 3);
if (stranded)
{
  myFilters.Add(c => c.Friends.Any(f => f.Cars.Any())); //friend has car
}
Expression<Func<Customer, bool>> filter = myFilters.AndTheseFiltersTogether();
IEnumerable<Customer> thoseCustomers = Data.Get(filter);
    public static Expression<Func<T, bool>> OrTheseFiltersTogether<T>(params Expression<Func<T, bool>>[] filters)
    {
        return filters.OrTheseFiltersTogether();
    }

    public static Expression<Func<T, bool>> OrTheseFiltersTogether<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 body = firstFilter.Body;
        var param = firstFilter.Parameters.ToArray();
        foreach (var nextFilter in filters.Skip(1))
        {
            var nextBody = Expression.Invoke(nextFilter, param);
            body = Expression.OrElse(body, nextBody);
        }
        Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(body, param);
        return result;
    }


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

    public static Expression<Func<T, bool>> AndTheseFiltersTogether<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 body = firstFilter.Body;
        var param = firstFilter.Parameters.ToArray();
        foreach (var nextFilter in filters.Skip(1))
        {
            var nextBody = Expression.Invoke(nextFilter, param);
            body = Expression.AndAlso(body, nextBody);
        }
        Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(body, param);
        return result;
    }