【问题标题】:C# Linq Lambda Expression for Entity Framework Query Passing Custom Expression to Where Condition用于实体框架查询的 C# Linq Lambda 表达式将自定义表达式传递到 Where 条件
【发布时间】:2020-03-30 07:43:24
【问题描述】:

我有两个名为 ShipmentType 和 Books 的表。已为这些表映射实体类。创建了另一个名为 BookShipment 的类,其中包含两个属性,即 ShipmentType 和 Book 类。

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

我试图创建如下的 where 表达式。

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();

where 子句中的上述表达式工作正常并从数据库中获取结果。

试图创建一个与上面 expr 表达式相同的动态表达式,但它给出了错误。

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();

执行上述 Where 子句时会报错。

{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.

【问题讨论】:

    标签: c# entity-framework linq expression


    【解决方案1】:

    当您创建一个表达式以传递给 LINQ where 函数时,请注意以下提示:

    类型是Expression&lt;Func&lt;T,Bool&gt;&gt; ...

    这意味着你有一个 T 类型的参数,并且你返回一个布尔值

    如果你创建了两个具有这种类型的 Lambda,并且你想将它们组合起来......即使你有相同的类型 T 作为参数,两个参数实例也不相同......你将不得不遍历树并替换参数,所以只有一个实例......

    如果你想要一个示例代码...给你...

    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; }
        }
    }
    

    当然你会想要添加错误处理等...

    【讨论】:

    • 如果这充分回答了您的问题,请单击它旁边的复选标记以标记答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-30
    相关资源
    最近更新 更多