【问题标题】:How can I create an Lambda Expression for Linq in the Where clause for two tables after the Join?如何在 Join 后的两个表的 Where 子句中为 Linq 创建 Lambda 表达式?
【发布时间】:2018-11-15 19:44:34
【问题描述】:

我有这个,

 Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
 Func<MyCourse, bool> funcWhere = filter.Compile();

然后这个,

var myClasses = db.MyCourse.Join(db.People, mc => mc.PersonId, 
p => p.PersonId, (mc, p) => new { MyCourse= mc, Person = p }).Where(???)

我需要这样做的原因是因为如果我首先将过滤器放在 MyCourse 表上,

db.MyCourse.Where(funcWhere).Join....

创建的 SQL 带回所有人员和所有 MyCourse,然后使用过滤器。如果我在最后做 where,

(mc, p) => new { MyCourse= mc, Person = p }).Where(mc=>mc.MyCourse.Active == 1)

我得到了一个很好的连接查询。否则引擎首先将所有行查询到内存中。包含数千行的两个独立查询。

我在 SO 和其他地方看到了大量关于此的问题。当有多个表时,我找不到一个告诉我如何使用动态Where Expression&lt;Func&lt;T,TResult&gt;&gt; 从联接中执行表达式。

目标是基于表达式(不是Dynamic Linq,也没有第三方)进行动态查询语句。事实上,这个问题声称最后的 Where 速度较慢,但​​在我的程序中它执行了正确的查询与联接。

MyCourse 有一个 PersonId,而 People 有一个 PersonId。如果我手写这个看起来像,

select mc.CourseName, p.LastName 
from MyCourse mc inner join Person p on mc.PersonId = p.PersonId
where mc.Active = 1;

(这些只是问题的示例列。除了 Active == 1 之外,它们并不是我真正想要的上述查询。)

Where clause with Join in lambda expression

更新:FWIW,我能够让它工作这个,

    var param = Expression.Parameter(typeof(MyClass), "MyClassDebug");
    var exp = Expression.Lambda<Func<MyClass, bool>>(
        Expression.Equal(
            Expression.Property(param, dbParameter),
            Expression.Constant(dbValue)
        ),
        param
    );

我没有做导航属性或其他任何事情。而且我可以这样使用它,

var MyQuery = (from recs in dbcontext.MyClass.Where(exp)
               ...three joins

生成的 SQL 看起来不错,Explain 计划显示最少的行检索。

【问题讨论】:

  • 你在使用实体框架吗?你的MyCourse 表中的PeopleId 是外键吗?
  • 是的。它只是名称上的外键,而不是这样定义的数据库中的外键。
  • 我明白了。如果是这样的话会更容易,因为 Entity Framework 会从您的 Person 类为您创建一个参考列表。但无论如何,还是可以做到的。
  • 首先,查询的开始应该是db.MyCourse.Where(filter)。但我认为根据您的 EF 版本,您应该寻找各种全局查询过滤器选项。
  • @GertArnold 问题是当我以这种方式使用它时,Linq 会带回所有 Person 记录,36k 然后调整活动课程的行。如果我将过滤器放在它的末尾并输入 x=>x.MyCourse.Active == 1 的表达式,那么我会得到一个带有 Join 的查询(从我在 SQL Profiler 上看到的)。

标签: c# entity-framework linq lambda expression-trees


【解决方案1】:

我怀疑在您的Expression 上拨打Compile() 会给您带来麻烦。您的完整查询包括Join,但您已经编译了Where 子句,因此它不能一起编译包括Join 在内的整个查询。这可能是它抓取整个表的原因,因为它先自己执行Where,然后再执行Join

但您无需致电Compile()。只需将Expression 传递给Where()

Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
var myClasses = db.MyCourse
    .Where(filter)
    .Join(db.People, mc => mc.PersonId, 
p => p.PersonId, (mc, p) => new { MyCourse= mc, Person = p }).ToList();

与您的实际问题有些无关,但如果您创建了外键,则可以稍微简化一下。如果您还没有更新您的 Visual Studio 项目中的模型。您的 Person 类将更改为具有 MyCourse 的列表,而您的 MyCourse 类将具有 Person 的列表。

所以你可以这样做:

Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
var courses = db.MyCourse.Include("Person").Where(filter);
foreach (var course in courses) {
    var person = course.Person; //This is populated with the Person record
}

Linq 处理连接,返回的每个 MyCourse 都将有一个 Person 属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-17
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    • 1970-01-01
    • 2017-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多