简体   繁体   English

EF Core 3.0 中的 LINQ 重大更改。 使用表达式

[英]LINQ Breaking changes in EF Core 3.0. Using expressions

In my app I have some queries that use the same repeated logic:在我的应用程序中,我有一些使用相同重复逻辑的查询:

var someThings = context.table1
  .where(SomeLogic)
  .ToList();

With EF Core 2.1 I could encapsulate this logic in a layer with all these expressions:使用 EF Core 2.1,我可以将此逻辑封装在具有所有这些表达式的层中:

public static Expression<Func<MyObject, bool>> SomeLogic =>
            myObject => myObject.CreationDate.Date == DateTime.Now.Date
                    && (myObject.Whatever.HasValue || myObject.MoreWhatever);

Now I discovered this was being evaluated in memory, and that's bad.现在我发现这是在内存中评估的,这很糟糕。

If I do something like:如果我做这样的事情:

var someThings = context.table1
  .where(myObject => 
      myObject.CreationDate.Date == DateTime.Now.Date
      && (myObject.Whatever.HasValue || myObject.MoreWhatever))
  .ToList();

then the query is evaluated in the DB, but I am putting some logic in the wrong layer.然后在数据库中评估查询,但我在错误的层中放置了一些逻辑。

I tried to subsitute Expression with a function or any other tool, but I don't find a way to do it.我试图用函数或任何其他工具代替Expression ,但我没有找到方法。

Is there a way to encapsulate the logic of a query in a layer as I was doing before, but preserving EF rules so that this query can still be evaluated in the DB?有没有办法像我之前所做的那样将查询的逻辑封装在一个层中,但保留 EF 规则以便仍然可以在数据库中评估此查询?

Thanks.谢谢。

Why you need a "real" expression and not just a Lambda is explained in this answer .这个答案解释了为什么你需要一个“真实”的表达式而不仅仅是一个 Lambda。 The created Expression can be created anywhere and passed as a parameter to the function that executes the query.创建的 Expression 可以在任何地方创建并作为参数传递给执行查询的函数。

This answer should guide the way you need to go.这个答案应该指导你需要去的方式。 You only have to replace the two dummy expressions with the whatever.hasvalue...stuff你只需要用whatever.hasvalue...stuff替换两个虚拟表达式

var param = Expression.Parameter(typeof(MyObject), nameof(MyObject));

//    myObject.CreationDate.Date == DateTime.Now.Date
Expression dateExpression = Expression.Equal(Expression.Constant(DateTime.Now),
    Expression.PropertyOrField(param, "CreationDate"));


var dummyExpression1 = Expression.Equal(Expression.Constant(1), Expression.Constant(1));
var dummyExpression2 = Expression.Equal(Expression.Constant(1), Expression.Constant(1));

//    && (myObject.Whatever.HasValue || myObject.MoreWhatever)
Expression orExpression = Expression.Or(dummyExpression1, dummyExpression2);


Expression allConditions = Expression.And(dateExpression, orExpression);

//myObject =>
Expression myExpression = Expression.Lambda<Func<MyObject, bool>>(allConditions, param);

var someThings = context.table1
    .where(myExpression)
    .ToList();

I had the most trouble with Expression.PropertyOrField.我在 Expression.PropertyOrField 上遇到了最大的麻烦。 If you have nested structures you need to loop through the data structure and call Expression.PropertyOrField with the first parameter being the result from the previous call to Expression.PropertyOrField.如果您有嵌套结构,您需要遍历数据结构并调用 Expression.PropertyOrField,第一个参数是上次调用 Expression.PropertyOrField 的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM