C# 将复杂LINQ平坦化为SQL
我有一个有点复杂的LINQ到SQL查询,我正在尝试优化它(不,不是过早地,事情很慢),它有点像这样C# 将复杂LINQ平坦化为SQL,c#,linq,C#,Linq,我有一个有点复杂的LINQ到SQL查询,我正在尝试优化它(不,不是过早地,事情很慢),它有点像这样 IQueryable<SearchListItem> query = DbContext.EquipmentLives .Where(...) .Select(e => new SearchListItem { EquipmentStatusId = e.EquipmentStatuses.FirstOrDefault(s => s.Date
IQueryable<SearchListItem> query = DbContext.EquipmentLives
.Where(...)
.Select(e => new SearchListItem {
EquipmentStatusId = e.EquipmentStatuses.FirstOrDefault(s => s.DateTo == null).Id,
StatusStartDate = e.EquipmentStatuses.FirstOrDefault(s => s.DateTo == null).DateFrom,
...
});
IQueryable query=DbContext.equipmentLifes
.其中(…)
.选择(e=>new SearchListItem{
EquipmentStatusId=e.EquipmentStatuses.FirstOrDefault(s=>s.DateTo==null).Id,
StatusStartDate=e.EquipmentStatuses.FirstOrDefault(s=>s.DateTo==null).DateFrom,
...
});
where子句并不重要,它们不过滤设备状态
,如果有人认为需要,它们很乐意包含在内
这是在相当大的一组表上,并返回一个相当详细的对象,有更多的引用到equipmentstatus
,但我相信您已经明白了。问题是很明显有两个子查询,我确信(除其他外)并不理想,特别是因为它们每次都是完全相同的子查询
有没有可能把这件事平淡一点?也许对数据库进行一些较小的查询并在
foreach
循环中创建SearchListItem
更容易些?以下是我对您的评论的看法,以及我所做的一些假设
- 它可能看起来很吓人,但是试一下,在GroupBy()之前有没有
)ToList()
- 如果您有LinqPad,请检查生成的SQL和查询数,或者只插入SQL Server探查器
- 使用LinqPad,您甚至可以放置一个
,以精确测量事物秒表
假设在调用
FirstOrDefault(s=>s.DateTo==null)后没有测试null值
我假设:
- 对于每个设备,总是有一个状态为
,或者DateTo==null
- 您只需要查看具有这种状态的设备
equipmentlifes
与equipmentstatus
连接起来,以避免子查询:
var query = DbContext.EquipmentLives
.Where(l => true)
.Join(DbContext.EquipmentStatuses.Where(s => s.DateTo == null),
eq => eq.Id,
status => status.EquipmentId,
(eq, status) => new SelectListItem
{
EquipmentStatusId = status.Id,
StatusStartDate = status.DateFrom
});
但是,如果您确实要执行
左连接
替换DbContext.EquipmentStatuses.Where(s=>s.DateTo==null)
与DbContext.EquipmentStatuses.Where(s=>s.DateTo==null.DefaultIfEmpty()
,则无需重复FirstOrDefault
。您可以添加中间Select
将其选中一次,然后重新使用:
IQueryable<SearchListItem> query = DbContext.EquipmentLives
.Where(...)
.Select(e => e.EquipmentStatuses.FirstOrDefault(s => s.DateTo == null))
.Select(s => new SearchListItem {
EquipmentStatusId = s.Id,
StatusStartDate = s.DateFrom,
...
});
不过,您的查询中还有另一个问题。如果EquipmentLive
中没有匹配的EquipmentStatus
,FirstOrDefault
将返回null
,这将导致最后一次选择中出现异常。因此,您可能需要一个附加的,其中
:
IQueryable<SearchListItem> query = DbContext.EquipmentLives
.Where(...)
.Select(e => e.EquipmentStatuses.FirstOrDefault(s => s.DateTo == null))
.Where(s => s != null)
.Select(s => new SearchListItem {
EquipmentStatusId = s.Id,
StatusStartDate = s.DateFrom,
...
});
正如您所说,较小的查询将显著提高您的性能。您可以通过SearchListItem中需要的字段获取这些设备状态的列表,然后使用foreach。您可以填写SearchListItem。如果您有一个复杂的LINQ查询,这可能意味着您有一个设计问题。要么缺少映射,要么查询实际上应该是数据库中的一个视图。这是个好主意,但假设
EquipmentStatus
对象是我可以用来填充SearchListItem
对象的基础。不幸的是,我需要将基础保留为EquipmentLifes
,因为SearchListItem
的许多其他属性需要EquipmentLifes
对象。这没有问题。您仍然可以使用e
访问它。尤其是在查询语法中,访问e非常简单。只需在select.Correct中使用它,但我不想将方法语法重写为查询语法,在您提供的方法语法示例中,equipmentlifes
不可访问。我认为您需要将其设置为一个选择多个,就像接受的答案一样。
var query =
from e in DbContext.EquipmentLives
where ...
let s = e.EquipmentStatuses.FirstOrDefault(s => s.DateTo == null)
select new SearchListItem {
EquipmentStatusId = s.Id,
StatusStartDate = s.DateFrom,
...
});
IQueryable<SearchListItem> query = DbContext.EquipmentLives
.Where(...)
.Select(e => e.EquipmentStatuses.FirstOrDefault(s => s.DateTo == null))
.Where(s => s != null)
.Select(s => new SearchListItem {
EquipmentStatusId = s.Id,
StatusStartDate = s.DateFrom,
...
});
var query =
from e in DbContext.EquipmentLives
where ...
let s = e.EquipmentStatuses.FirstOrDefault(s => s.DateTo == null)
where s != null
select new SearchListItem {
EquipmentStatusId = s.Id,
StatusStartDate = s.DateFrom,
...
});