How can you update a Linq Expression with additional parameters?

前端 未结 5 924
感情败类
感情败类 2020-12-29 14:28

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):



        
5条回答
  •  自闭症患者
    2020-12-29 14:45

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

    IEnumerable projects = unitOfWork.ProjectRepository.Get(filter);
    

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

    IQueryable 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> filter1 = x => x.A > 1;
            Expression> 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>(
                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> 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> Combine(Expression> filter1, Expression> 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>(
                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);
        }
    }
    

提交回复
热议问题