【问题标题】:Linq to entities Left JoinLinq 到实体左连接
【发布时间】:2010-12-03 00:23:22
【问题描述】:

我想在 Linq to Entities 中实现以下目标:

获取所有没有应用程序或应用程序状态为 != 4(已完成)的查询

select e.*
from Enquiry enq
left outer join Application app
 on enq.enquiryid = app.enquiryid
where app.Status <> 4 or app.enquiryid is null

之前有没有人在不使用 DefaultIfEmpty() 的情况下完成此操作,Linq to Entities 不支持该功能?

我正在尝试向 IQueryable 查询添加过滤器,如下所示:

IQueryable<Enquiry> query = Context.EnquirySet; 

query = (from e in query 
         where e.Applications.DefaultIfEmpty()
                             .Where(app=>app.Status != 4).Count() >= 1 
         select e);

谢谢 标记

【问题讨论】:

  • EF .NET 4.0 中包含 DefaultIfEmpty。
  • 感谢 Damien - 期待 .NET 4 中的 EF 增强功能

标签: linq linq-to-entities left-join outer-join


【解决方案1】:

由于 Linq 处理外部的愚蠢(阅读非标准)方式,您必须使用 DefaultIfEmpty()。

您要做的是将 Linq-To-Entities 查询运行到两个 IEnumerable 中,然后使用 DefaultIfEmpty() 向左加入它们。它可能看起来像:

IQueryable enq = Enquiry.Select();
IQueryable app = Application.Select();
var x = from e in enq
join a in app on e.enquiryid equals a.enquiryid
into ae
where e.Status != 4
from appEnq in ae.DefaultIfEmpty()
select e.*;

仅仅因为你不能用 Linq-To-Entities 来做这并不意味着你不能用原始 Linq 来做。

(注意:在有人反对我之前......是的,我知道有更优雅的方法可以做到这一点。我只是想让它易于理解。重要的是这个概念,对吧?)

【讨论】:

  • 当我过滤 IQueryable 结果以创建像这样的存储表达式时,这可能吗?:IQueryable query = Context.EnquirySet; query = (from e in query where e.Applications.DefaultIfEmpty().Where(app=>app.Status != 4).Count() >= 1 select e);
  • 嗯,在我的示例中似乎无法正常工作。你确定where子句是正确的吗?状态在应用实体上???谢谢
【解决方案2】:

这样做:

IQueryable<Enquiry> query = Context.EnquirySet; 

query = (from e in query 
         where (!e.Applications.Any()) 
               || e.Applications.Any(app => app.Status != 4)
         select e);

我根本没有发现 LINQ 对 SQL“愚蠢”中的“外部联接”问题的处理。理解它的关键是考虑具有可为空属性的对象图而不是表格结果集。

Any() 映射到 SQL 中的 EXISTS,因此在某些情况下它比 Count() 效率

【讨论】:

    【解决方案3】:

    感谢大家的帮助。我最终选择了这个选项,但您的解决方案有助于拓宽我的知识。

    IQueryable<Enquiry> query = Context.EnquirySet;
    
    query = query.Except(from e in query
                         from a in e.Applications
                         where a.Status == 4
                         select e);
    

    【讨论】:

      【解决方案4】:

      在 EF 4.0+ 中,LEFT JOIN 语法略有不同,并呈现出一种疯狂的怪癖:

      var query = from c1 in db.Category 
              join c2 in db.Category on c1.CategoryID equals c2.ParentCategoryID  
              into ChildCategory 
              from cc in ChildCategory.DefaultIfEmpty() 
              select new CategoryObject  
              { 
                  CategoryID = c1.CategoryID,  
                  ChildName = cc.CategoryName 
              } 
      

      如果您在 SQL Server Profiler 中捕获此查询的执行,您将看到它确实执行了 LEFT OUTER JOIN。但是,如果您的 Linq-to-Entity 查询中有多个 LEFT JOIN ("Group Join") 子句,我发现自联接子句实际上可能像在 INNER JOIN 中一样执行 - 即使使用了上述语法!

      解决方案?根据 MS 的说法,这听起来很疯狂,而且听起来是错误的,但我通过更改连接子句的顺序解决了这个问题。如果自引用 LEFT JOIN 子句是第一个 Linq Group Join,SQL Profiler 报告一个 INNER JOIN。如果自引用 LEFT JOIN 子句是 LAST Linq Group Join,SQL Profiler 会报告一个 LEFT JOIN。

      【讨论】:

      • 这个答案真的应该得到更多的业力
      【解决方案5】:

      要考虑的另一件事是,如果您直接从左连接组(使用 into 语法)引用 where 子句中的任何属性而不检查 null,实体框架仍会将您的 LEFT JOIN 转换为 INNER JOIN。

      为避免这种情况,请过滤查询的“from x in leftJoinedExtent”部分,如下所示:

      var y = from parent in thing
              join child in subthing on parent.ID equals child.ParentID into childTemp
              from childLJ in childTemp.Where(c => c.Visible == true).DefaultIfEmpty()
              where parent.ID == 123
              select new {
                  ParentID = parent.ID,
                  ChildID = childLJ.ID
              };
      

      匿名类型中的 ChildID 将是可为空的类型,并且由此生成的查询将是 LEFT JOIN。

      【讨论】:

        猜你喜欢
        • 2013-10-21
        • 1970-01-01
        • 1970-01-01
        • 2015-01-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-15
        相关资源
        最近更新 更多