简体   繁体   English

LINQ实体建立多层子查询的更有效方法

[英]linq to entities more efficient way to build multiple layers child query

I am using the EF linq to entities. 我对实体使用EF linq。 I need to get multiple layers of items for filling an treeview. 我需要获得多层填充树状视图的项目。 The code below is really slow and takes a long time to load. 下面的代码确实很慢,并且加载时间很长。 The query generated by the EF is like 200 lines (checked this with SQL Profiler). EF生成的查询大约有200行(已使用SQL Profiler进行了检查)。

I was wondering if there is an more efficient way of doing this, maybe with multiple queries or code reordering. 我想知道是否有更有效的方法来执行此操作,可能是通过多个查询或代码重新排序。 The performance is not really accaptable this way. 这种性能并不能真正实现。

DBContext db = _DbProvider.GetContext();
List<Level1TreeviewRegion> treeList = (
    from level1Item in db.Level1Table
     join customer in db.Clients on level1Item.Customer_ID equals customer.Customer_ID
     let rentableLocations = (from level4 in db.Location where level4.Rentable_Unit == "J" && level1Item.Customer_ID == level4.Customer_ID && level1Item.Level1_Id == level4.Level1_Id select level4).FirstOrDefault()
     orderby customer.Treeview_sort_level_1_by == "n" ? level1Item.Name : "", customer.Treeview_sort_level_1_by != "n" ? level1Item.Level1_Id : 0
     where level1Item.Customer_ID == customerAreaId && rentableLocations != null
     select new Level1TreeviewRegion
     {
         Record_Number = level1Item.Record_Number,
         LEVEL1_ID = level1Item.Level1_Id,
         LEVEL1_NAME_WithoutCodes = !string.IsNullOrEmpty(level1Item.Name) ? level1Item.Name.Trim() : "",
         LEVEL1_NAME_WithCodes = !string.IsNullOrEmpty(level1Item.Name) ? level1Item.Level1_Id + "-" + level1Item.Name.Trim() : "",
         SearchString = !string.IsNullOrEmpty(level1Item.Name) ? level1Item.Level1_Id + level1Item.Name.Trim() : "",
         Level2List = (from level2 in db.Level2Table
                     let rentableLocations1 = (from level4 in db.Location where level4.Rentable_Unit == "J" && level2.KP_PERSNEEL == level4.Customer_ID && level2.Level1_Id == level4.Level1_Id && level2.Level2_Id == level4.Level2_Id select level4).FirstOrDefault()
                     orderby customer.Treeview_sort_level_2_by == "n" ? level2.Name : "", customer.Treeview_sort_level_2_by != "n" ? level2.Level2_Id : ""
                     where level2.KP_PERSNEEL == customerAreaId && level2.Level1_Id == level1Item.Level1_Id && rentableLocations1 != null
                     select new Level2TreeviewRegion
                     {
                         Record_Number = level2.Record_Number,
                         LEVEL2_CODE = level2.Level2_Id.Trim(),
                         LEVEL2_NAME_WithoutCodes = !string.IsNullOrEmpty(level2.Name) ? level2.Name.Trim() : "",
                         LEVEL2_NAME_WithCodes = !string.IsNullOrEmpty(level2.Name) ? level2.Level2_Id.Trim() + "-" + level2.Name.Trim() : "",
                         SearchString = !string.IsNullOrEmpty(level2.Name) ? level2.Level2_Id.Trim() + level2.Name.Trim() : "",
                         LEVEL1_ID = level2.Level1_Id,
                         Level3List = (from level3 in db.Level3Table
                                          let rentableLocations2 = (from level4 in db.Location where level4.Rentable_Unit == "J" && level3.Customer_ID == level4.Customer_ID && level3.Level1_Id == level4.Level1_Id && level3.Level2_Id == level4.Level2_Id && level3.Level3_Id == level4.Level3_Id select level4).FirstOrDefault()
                                          let objectsInObjectModule = (from om in db.Object_Module where om.Customer_ID == level2.KP_PERSNEEL && om.Level2_Id == level2.Level2_Id && om.Level1_Id == level2.Level1_Id && om.ModuleId == WishModules.VB2.ToString() select om.Level3_Id)
                                          orderby customer.Treeview_sort_level_3_by == "n" ? level3.Name : "", customer.Treeview_sort_level_3_by != "n" ? level3.Level3_Id : ""
                                          where (level3.Level2_Id.Trim() == level2.Level2_Id.Trim() && level3.Customer_ID == customerAreaId
                                                 // extra check with the LEVEL3_MODULE table, it was Object.MJP = J, now it needs a record in the LEVEL3_MODULE table
                                                 //so if no objects are found, it is good (all are visible) OR objects are found, only they are visible
                                                 && (!objectsInObjectModule.Any() || objectsInObjectModule.Contains(level3.Level3_Id)) && rentableLocations2 != null)
                                          select new Level3TreeviewRegion
                                          {
                                              Record_Number = level3.Record_Number,
                                              LEVEL1_ID = level3.Level1_Id,
                                              LEVEL2_CODE = level3.Level2_Id.Trim(),
                                              LEVEL3_CODE = level3.Level3_Id.Trim(),
                                              LEVEL3_NAME_WithoutCodes = !string.IsNullOrEmpty(level3.Name) ? level3.Name.Trim() : "",
                                              LEVEL3_NAME_WithCodes = !string.IsNullOrEmpty(level3.Name) ? level3.Level3_Id.Trim() + "-" + level3.Name.Trim() : "",
                                              SearchString = !string.IsNullOrEmpty(level3.Name) ? level3.Level3_Id.Trim() + level3.Name.Trim() : "",
                                              LEVEL3_PLAATS = level3.City.Trim(),
                                              Ownership = string.IsNullOrEmpty(level3.Ownership) == false && (new[] { "j", "ja", "y", "yes" }).Contains(level3.Ownership.ToLower().Trim()),
                                              Level4List = (from level4 in db.Level4Table
                                                              orderby customer.Treeview_sort_level_4_by == "n" ? level4.Description : "", customer.Treeview_sort_level_4_by != "n" ? level4.Code : ""
                                                              where level4.Rentable_Unit == "J" && level3.Level2_Id.Trim() == level4.Level2_Id.Trim() && level3.Customer_ID == level4.Customer_ID && level3.Level1_Id == level4.Level1_Id && level3.Level3_Id == level4.Level3_Id
                                                              select new Level4TreeviewRegion
                                                              {
                                                                  Record_Number = level4.Record_Number,
                                                                  LEVEL1_ID = level3.Level1_Id,
                                                                  LEVEL2_CODE = level3.Level2_Id.Trim(),
                                                                  LEVEL3_CODE = level3.Level3_Id.Trim(),
                                                                  LEVEL4_CODE = level4.Code.Trim(),
                                                                  LEVEL4_NAME_WithoutCodes = !string.IsNullOrEmpty(level4.Description) ? level4.Description.Trim() : "",
                                                                  LEVEL4_NAME_WithCodes = !string.IsNullOrEmpty(level4.Description) ? level4.Code.Trim() + "-" + level4.Description.Trim() : "",
                                                                  SearchString = !string.IsNullOrEmpty(level4.Description) ? level4.Code.Trim() + level4.Description.Trim() : "",
                                                              }).ToList()
                                          }).ToList()
                     }).ToList()
     }).ToList();

The relevant database part: 相关数据库部分:

Level1Table
[RECNUM] [int] NOT NULL IDENTITY(1, 1),
[Level1_Id] [smallint] NOT NULL,

Level2Table
[RECNUM] [int] NOT NULL IDENTITY(1, 1),
[Level1_Id] [smallint] NOT NULL,
[Level2_Id] [char] (10),
[NAAM1] [char] (60),

Level3Table
[RECNUM] [int] NOT NULL IDENTITY(1, 1),
[Level1_Id] [smallint] NOT NULL,
[Level2_Id] [char] (10),
[Level3_Id] [char] (10),
[NAAM] [char] (60),

Level4Table
[RECNUM] [int] NOT NULL IDENTITY(1, 1),
[Level1_Id] [smallint] NOT NULL,
[Level2_Id] [char] (10) COLLATE,
[Level3_Id] [char] (10) COLLATE,
[Level4_Id] [char] (10) COLLATE,

Here are a few things you can do: 您可以执行以下几项操作:

Move the logic of the property population into constructors: 将属性填充的逻辑移到构造函数中:

public class Level1TreeviewRegion
{
    public Level1TreeviewRegion(Level1Item level1Item)
    {
        Record_Number = level1Item.Record_Number;
        LEVEL1_ID = level1Item.Level1_Id;
        LEVEL1_NAME_WithoutCodes = !string.IsNullOrEmpty(level1Item.Name) ? level1Item.Name.Trim() : "";
        LEVEL1_NAME_WithCodes = !string.IsNullOrEmpty(level1Item.Name) ? level1Item.Level1_Id + "-" + level1Item.Name.Trim() : "";
        SearchString = !string.IsNullOrEmpty(level1Item.Name) ? level1Item.Level1_Id + level1Item.Name.Trim() : "";
    }
    //...
}

Do this for all the Level[x]TreeViewRegion classes. 对所有Level[x]TreeViewRegion类执行此操作。

Turn rentablelocations into a count and use it on the where statement: rentablelocations转换为计数并在where语句中使用它:

List<Level1TreeviewRegion> treeList = (
        from level1Item in db.Level1Table
        join customer in db.Clients on level1Item.Customer_ID equals customer.Customer_ID            
        orderby 
            customer.Treeview_sort_level_1_by == "n" ? level1Item.Name : "", 
            customer.Treeview_sort_level_1_by != "n" ? level1Item.Level1_Id : "0"
        where level1Item.Customer_ID == customerAreaId &&
        db.Location.Count(level4 =>
            level4.Rentable_Unit == "J" &&
            level1Item.Customer_ID == level4.Customer_ID &&
            level1Item.Level1_Id == level4.Level1_Id
        ) > 0

Do the same for rentableLocations1 and rentableLocations2 . rentableLocations1rentableLocations2进行相同的操作。

objectsInObjectModule does not need to be part of the level 3 subquery. objectsInObjectModule不必是3级子查询的一部分。 It only uses stuff from level2 so you can move it up one level. 它仅使用level2中的内容,因此您可以将其上移一级。

Remove the ToList() calls all over the place. 删除整个位置的ToList()调用。 You only need the last one. 您只需要最后一个。

The end result should be something along the lines of: 最终结果应类似于以下内容:

List<Level1TreeviewRegion> treeList = (
        from level1Item in db.Level1Table
        join customer in db.Clients on level1Item.Customer_ID equals customer.Customer_ID            
        orderby 
            customer.Treeview_sort_level_1_by == "n" ? level1Item.Name : "", 
            customer.Treeview_sort_level_1_by != "n" ? level1Item.Level1_Id : "0"
        where level1Item.Customer_ID == customerAreaId &&
        db.Location.Count(level4 =>
            level4.Rentable_Unit == "J" &&
            level1Item.Customer_ID == level4.Customer_ID &&
            level1Item.Level1_Id == level4.Level1_Id
        ) > 0
        select new Level1TreeviewRegion(level1Item)
        {
            Level2List = (from level2 in db.Level2Table                             
                          let objectsInObjectModule =
                            (
                                from om in db.Object_Module
                                where
                                    om.Customer_ID == level2.KP_PERSNEEL &&
                                    om.Level2_Id == level2.Level2_Id &&
                                    om.Level1_Id == level2.Level1_Id &&
                                    om.ModuleId == WishModules.VB2.ToString()
                                select om.Level3_Id
                            )
                          orderby customer.Treeview_sort_level_2_by == "n" ? level2.Name : "", customer.Treeview_sort_level_2_by != "n" ? level2.Level2_Id : ""
                          where 
                            level2.KP_PERSNEEL == customerAreaId && 
                            level2.Level1_Id == level1Item.Level1_Id && 
                            db.Location.Count(level4 =>
                                level4.Rentable_Unit == "J" &&
                                level2.KP_PERSNEEL == level4.Customer_ID &&
                                level2.Level1_Id == level4.Level1_Id &&
                                level2.Level2_Id == level4.Level2_Id
                            ) > 0
                          select new Level2TreeviewRegion(level2)
                          {
                              Level3List = (from level3 in db.Level3Table                                                                                                
                                            orderby 
                                                customer.Treeview_sort_level_3_by == "n" ? level3.Name : "", 
                                                customer.Treeview_sort_level_3_by != "n" ? level3.Level3_Id : ""
                                            where 
                                                (
                                                    level3.Level2_Id.Trim() == level2.Level2_Id.Trim() && 
                                                    level3.Customer_ID == customerAreaId
                                                   // extra check with the LEVEL3_MODULE table, it was Object.MJP = J, now it needs a record in the LEVEL3_MODULE table
                                                   //so if no objects are found, it is good (all are visible) OR objects are found, only they are visible
                                                   && (
                                                        !objectsInObjectModule.Any() || 
                                                        objectsInObjectModule.Contains(level3.Level3_Id)
                                                      ) 
                                                   && db.Location.Count(level4 =>
                                                    level4.Rentable_Unit == "J" &&
                                                    level3.Customer_ID == level4.Customer_ID &&
                                                    level3.Level1_Id == level4.Level1_Id &&
                                                    level3.Level2_Id == level4.Level2_Id &&
                                                    level3.Level3_Id == level4.Level3_Id) > 0
                                                )
                                            select new Level3TreeviewRegion(level3)
                                            {
                                                Level4List = (from level4 in db.Level4Table
                                                              orderby 
                                                              customer.Treeview_sort_level_4_by == "n" ? level4.Description : "", 
                                                              customer.Treeview_sort_level_4_by != "n" ? level4.Code : ""
                                                              where 
                                                                level4.Rentable_Unit == "J" && 
                                                                level3.Level2_Id.Trim() == level4.Level2_Id.Trim() && 
                                                                level3.Customer_ID == level4.Customer_ID && 
                                                                level3.Level1_Id == level4.Level1_Id && 
                                                                level3.Level3_Id == level4.Level3_Id
                                                              select new Level4TreeviewRegion(level4, level3))
                                            })
                          })
        }).ToList();

From past experience, when you end up with complex queries and logic that looks strikingly similar, it usually means you need to take a step back and rethink your design. 根据过去的经验,当您遇到看起来非常相似的复杂查询和逻辑时,通常意味着您需要退后一步,重新考虑设计。

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

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