【问题标题】:How to combine 4 entity tables into one expression?如何将4个实体表组合成一个表达式?
【发布时间】:2018-09-24 01:36:27
【问题描述】:

我需要帮助了解如何在 Linq-to-Entities 语句中执行多个连接。我总是很难理解 Linq 连接的表达式,尤其是多重连接。我总是最终使用子查询。例如:

public List<MyReportItem> GetReportItemsHelper(string[] years, string[] quarters, string[] areas, string myType, string[] ownerships, IEnumerable<String> fieldCodes)
{
    var _reportItems = _db.MytableEntity1
        .Where(c => c.FieldID.Equals(MY_ID)             //Id Field
            && years.Contains(c.PeriodYear)             //PeriodYear/Year
            && quarters.Contains(c.Period)              //Period/Quarters
            && c.MyType.Equals(myType)                  //Special Type
            && areas.Contains(c.Area)                   //Area                   
            && ownerships.Contains(c.Ownership))        //Ownership
        .Where(c => fieldCodes.Contains(c.FieldCode))   //Field Code
        .Where(c => c.Suppress.Equals("0"))             //Suppression is false
        .Select(c => new MyReportItem
        {
            Field1 = c.FieldA,
            Field2 = c.FieldB,
            Field3 = c.FieldC.TrimEnd(),

            //Sub-queries
            Field4 = _db.MytableEntity2.Where(g => g.FieldID.Equals(c.FieldID) && g.MyType.Equals(c.MyType) && g.Area.Equals(c.Area)).Select(g => g.AreaName.TrimEnd()).FirstOrDefault(),
            Field5 = _db.MytableEntity3.Where(o => o.Ownership.Equals(c.Ownership)).Select(o => o.OwnerTitle.TrimEnd()).FirstOrDefault(),                   
            Field6 = _db.MytableEntity4.Where(i => c.FieldCode.Equals(i.FieldCode) && myType.Equals(c.MyType)).Select(i => i.FieldTitle).FirstOrDefault(),
            Field7 = _db.MytableEntity4.Where(i => c.FieldCode.Equals(i.FieldCode) && myType.Equals(c.MyType)).Select(i => i.FieldLevel).FirstOrDefault(),

            Field8 = c.FieldD,
            Field9 = c.FieldE,
            Field10 = c.FieldF,
            Field11 = c.FieldG,
            Field12 = c.FieldH,
            Field13 = c.FieldI,
            Field14 = c.FieldJ,
            Field15 = c.FieldK
        }).Distinct().ToList();

    return _reportItems; //return report detail items
}

但现在我面临着一个非常大的数据库(可能有 6000 万条记录或更多?),并且子查询正在成为我们响应时间的瓶颈。我想解决这个问题并利用“加入”。我看到很多示例展示了如何执行连接,但我几乎看不到任何带有 multiple 连接的示例。

如何对“MytableEntity2”、“MytableEntity3”、“MytableEntity4”执行多个连接(在新的“MyReportItem”内),以消除字段 4-7 的子查询?如何将 4 个实体表组合成一个表达式?谢谢!

【问题讨论】:

  • 请参阅stackoverflow.com/a/41274884/2557128,但请注意,通常在 EF 中您根本不需要使用连接,您应该在主对象上具有导航属性以拉入其他相关对象(表)。跨度>
  • @NetMage 你有例子吗?当我在选择之前进行连接时,它会破坏我的选择。具体来说, .Select(c => new MyReportItem { }) 不再按预期工作。你能展示一下它是如何工作的吗?
  • 您能否更具体地了解“不再按预期工作”的意思?我看到您的子查询中有FirstOrDefault,在Join 中可能会有不同的处理方式。
  • 您不需要更改数据库。如果您不想更改实体,则需要使用Join。顺便说一句,请注意,您在 MytableEntity4 的子查询中对 myType 进行了冗余测试。

标签: c# join lambda entity-framework-6 linq-to-entities


【解决方案1】:

您可以使用GroupJoin 执行与FirstOrDefault 的子查询相同的操作。请注意,如果您知道每个子查询会产生 1 个匹配项,则可以使用 Join 而不使用 FirstOrDefault

var rpt = MytableEntity1
        .Where(c => c.FieldID.Equals(MY_ID)             //Id Field
            && years.Contains(c.PeriodYear)             //PeriodYear/Year
            && quarters.Contains(c.Period)              //Period/Quarters
            && c.MyType.Equals(myType)                  //Special Type
            && areas.Contains(c.Area)                   //Area                   
            && ownerships.Contains(c.Ownership))        //Ownership
        .Where(c => fieldCodes.Contains(c.FieldCode))   //Field Code
        .Where(c => c.Suppress.Equals("0"))             //Suppression is false
        .GroupJoin(MytableEntity2, c => new { c.FieldID, c.MyType, c.Area }, g => new { g.FieldID, g.MyType, g.Area }, (c, gj) => new { c, g = gj.Select(g => g.AreaName.TrimEnd()).FirstOrDefault() })
        .GroupJoin(MytableEntity3, cg => cg.c.Ownership, o => o.Ownership, (cg, oj) => new { cg.c, cg.g, o = oj.Select(o => o.OwnerTitle.TrimEnd()).FirstOrDefault() })
        .GroupJoin(MytableEntity4, cgo => cgo.c.FieldCode, i => i.FieldCode, (cgo, ij) => new { cgo.c, cgo.g, cgo.o, i = ij.FirstOrDefault() })
        .Select(cgoi => new MyReportItem {
            Field1 = cgoi.c.FieldA,
            Field2 = cgoi.c.FieldB,
            Field3 = cgoi.c.FieldC.TrimEnd(),
            Field4 = cgoi.g,
            Field5 = cgoi.o,
            Field6 = cgoi.i.FieldTitle,
            Field7 = cgoi.i.FieldLevel,
            Field8 = cgoi.c.FieldD,
            Field9 = cgoi.c.FieldE,
            Field10 = cgoi.c.FieldF,
            Field11 = cgoi.c.FieldG,
            Field12 = cgoi.c.FieldH,
            Field13 = cgoi.c.FieldI,
            Field14 = cgoi.c.FieldJ,
            Field15 = cgoi.c.FieldK
        }).Distinct().ToList();

【讨论】:

  • 你知道GroupJoin和Join的区别吗?这太棒了。我还没有实现 GroupJoin(还),但是常规的 Joins 极大地改善了响应时间!
  • 这才是我真正追求的:c => new { c.FieldID, c.MyType, c.Area },看来直到现在我都无法理解它。太棒了,谢谢!!
  • Join 只会在左右两边都匹配(内连接)的情况下返回结果,并且每个左右组合都会返回一个结果。 GroupJoin 可以让你组合一个 left + 一个 IEnumerable 的所有匹配权限,可用于模拟左外连接或 join+group by。
  • 这对我帮助很大,我的同事们还在摸不着头脑......
猜你喜欢
  • 1970-01-01
  • 2013-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-31
  • 2014-08-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多