[英]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
. 对
rentableLocations1
和rentableLocations2
进行相同的操作。
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.