C# Linq Lambda Expression for Entity Framework Query Passing Custom Expression to Where Condition

流过昼夜 提交于 2021-02-10 14:51:10

问题


I have two tables called ShipmentType and Books. Entity class has been mapped for these tables. Created another class called BookShipment which contains two properties, classes of ShipmentType and Book.

public class BookShipment
{
    public ShipmentType Shipment { get; set; }
    public Books Book { get; set; }
}

I was trying to create a where expression as follows.

Expression<Func<BookShipment, bool>> expr = x => (x.Shipment.ID == 1 && x.Book.ID == 1);

            var result = from c in styp
                         join d in book
                          on c.ID equals d.ID                         
                         select new BookShipment { Shipment = c, Book = d };

var List = result.Where(expr).ToList();

and above expression at where clause is working fine and getting the result from database.

Tried to create a dynamic expression same as above expr expression but it is giving error.

BookShipment table = new BookShipment();
table.Shipment = new ShipmentType();
table.Book = new Books();

ParameterExpression ParameterL = Expression.Parameter(table.GetType(), "x");

ParameterExpression Parameter1 = Expression.Parameter(table.Shipment.GetType(), "x.Shipment");
ParameterExpression Parameter2 = Expression.Parameter(table.Book.GetType(), "x.Book");

var Property1 = Expression.Property(Parameter1, "ID");
var Property2 = Expression.Property(Parameter2, "ID");

var Clause1 = Expression.Equal(Property1, Expression.Constant(1));
var Clause2 = Expression.Equal(Property2, Expression.Constant(1));

var Lambda1 = Expression.Lambda<Func<ShipmentType, bool>>(Clause1, Parameter1);
var Lambda2 = Expression.Lambda<Func<Books, bool>>(Clause2, Parameter2);

var OrElseClause = Expression.Or(Lambda1.Body, Lambda2.Body);
var Lambda = Expression.Lambda<Func<BookShipment, bool>>(OrElseClause, ParameterL);

            var result = from c in styp
                         join d in book
                          on c.ID equals d.ID                         
                         select new BookShipment { Shipment = c, Book = d };

var record = result.Where(Lambda).ToList();

When executing above Where clause it is giving error.

{System.InvalidOperationException: The LINQ expression 'DbSet<ShipmentType>
    .Join(
        outer: DbSet<Books>, 
        inner: s => s.ID, 
        outerKeySelector: b => b.BookID, 
        innerKeySelector: (s, b) => new TransparentIdentifier<ShipmentType, Books>(
            Outer = s, 
            Inner = b
        ))
    .Where(ti => ti.Shipment.ID == 1 || ti.Book.BookID == 1)' could not be translated.

回答1:


when you create an expression to pass into the LINQ where function, take the following hints:

the type is Expression<Func<T,Bool>> ...

that means you have ONE parameter of type T, and you return a bool

if you create two Lambdas with this type, and you want to combine them... even though you have the same type T as parameter, the two parameter instances are not the same ... you will have to traverse the tree and replace the parameter so there is only one instance...

if you'd like an example code ... here you go...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace SoExamples.ExpressionTrees
{
    class Program
    {
        static void Main(string[] args)
        {

            var expr1 = GetExpression<Func<A, bool>>(x => x.Prop1 == 42);
            var expr2 = GetExpression<Func<A, bool>>(x => x.Prop2 == "foo");
            var expr3 = GetConstComparison<A, int>("Prop3.Prop1", 123);

            var test = new A { Prop1 = 42, Prop2 = "foo", Prop3 = new B { Prop1 = 123 } };

            var f1 = expr1.Compile();
            var t1 = f1(test);
            var f2 = expr2.Compile();
            var t2 = f2(test);
            var f3 = expr3.Compile();
            var t3 = f3(test);

            Expression tmp = Expression.AndAlso(Expression.AndAlso(expr1.Body, expr2.Body), expr3.Body);
            tmp = new ParamReplaceVisitor(expr2.Parameters.First(), expr1.Parameters.First()).Visit(tmp);
            tmp = new ParamReplaceVisitor(expr3.Parameters.First(), expr1.Parameters.First()).Visit(tmp);
            var expr4 = Expression.Lambda<Func<A, bool>>(tmp, expr1.Parameters.First());

            var f4 = expr4.Compile();
            var t4 = f4(test);

            var list = new List<A> { test };

            var result = list.AsQueryable().Where(expr4).ToList();

        }

        static Expression<TDelegate> GetExpression<TDelegate>(Expression<TDelegate> expr)
        {
            return expr;
        }
        static Expression<Func<T, bool>> GetConstComparison<T, P>(string propertyNameOrPath, P value)
        {
            ParameterExpression paramT = Expression.Parameter(typeof(T), "x");
            Expression expr = getPropertyPathExpression(paramT, propertyNameOrPath.Split('.'));
            return Expression.Lambda<Func<T, bool>>(Expression.Equal(expr, Expression.Constant(value)), paramT);
        }

        private static Expression getPropertyPathExpression(Expression expr, IEnumerable<string> propertyNameOrPath)
        {
            var mExpr = Expression.PropertyOrField(expr, propertyNameOrPath.First());
            if (propertyNameOrPath.Count() > 1)
            {
                return getPropertyPathExpression(mExpr, propertyNameOrPath.Skip(1));
            }
            else
            {
                return mExpr;
            }
        }
    }

    public class ParamReplaceVisitor : ExpressionVisitor
    {
        private ParameterExpression orig;
        private ParameterExpression replaceWith;

        public ParamReplaceVisitor(ParameterExpression orig, ParameterExpression replaceWith)
        {
            this.orig = orig;
            this.replaceWith = replaceWith;
        }
        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (node == orig)
                return replaceWith;
            return base.VisitParameter(node);
        }
    }

    public class A
    {
        public int Prop1 { get; set; }
        public string Prop2 { get; set; }
        public B Prop3 { get; set; }
    }

    public class B
    {
        public int Prop1 { get; set; }
    }
}

of course you will want to add errorhandling etc...



来源:https://stackoverflow.com/questions/60926048/c-sharp-linq-lambda-expression-for-entity-framework-query-passing-custom-express

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!