如何编写需要子查询的 Linq 查询?
How do I write a Linq query that needs a subquery?
我需要从数据库中提取一些关于支持票的信息。每张票都与一个医学成像系统相关联,每个系统可能有也可能没有与之相关的服务范围。如果是,则可能有多个服务封面条目,但只有一个我们感兴趣。
我知道这不是有效的 Linq,但我真正想做的是以下...
var tickets = cxt.SupportTickets
.Select( t => new {
ID = t.ID,
Customer = t.Customer.Name,
var cover = t.System.CoverItems.FirstOrDefault(ci => // some query)
CoverLevel = cover?.Level.Name,
Expiry = cover?.Expiry.ToLongDateString()
});
有什么办法吗?我知道我可以为封面中想要的每一位数据重复 t.CoverItems.FirstOrDefault(...)
位,但是除了这会产生绝对糟糕的代码混乱之外,它会非常低效,因为它需要做同样的事情对每张票进行多次子查询。
我考虑过将其全部分解为一个 foreach
循环,但后来我看不出如何创建 tickets
集合。我不能创建一个空集合然后向其中添加对象,因为它们是匿名类型,我不想考虑您将如何指定泛型类型!
有人有什么想法吗?
您可以提高可读性:
var tickets = cxt.SupportTickets
.Select(t => new {
Ticket = t,
CoverItem = t.System.CoverItems.FirstOrDefault(ci => // some query)
})
.Select(x => new {
ID = x.Ticket.ID,
Customer = x.Ticket.Customer.Name,
CoverLevel = x.CoverItem?.Level.Name,
Expiry = x.CoverItem?.Expiry.ToLongDateString()
});
您可以使用查询符号来代替使用 let
子句:
var query=from t in cxt.SupportTickets
let cover = t.System.CoverItems.FirstOrDefault(ci => some query)
select new {
ID = t.ID,
Customer = t.Customer.Name,
CoverLevel = cover?.Level.Name,
Expiry = cover?.Expiry//.ToLongDateString()
};
最后将做与@TimSchmelter 回答相同的事情,但对于类似的事情,您可以使用 let
。另一件事,我几乎可以肯定 ToLongDateString()
方法在 EF 中不受支持。
这个我试过了(如果你想单独开发子查询,因为SoC原理):
var innerQuery = cxt.SupportTickets
.Where(artist => artist.coverId == SomeParameter)
.Select(artist => new {
artistId = artist.artistId,
artistCompleteName = artist.artistName,
artistMasterPiece = artist.CoverName
});
var tickets = cxt.SupportTickets
.Where(
t => innerQuery.Contains(t.coverId)
)
.Select( t => new {
ID = t.ID,
Customer = t.Customer.Name,
var cover = t.System.CoverItems.FirstOrDefault()
CoverLevel = cover?.Level.Name,
Expiry = cover?.Expiry.ToLongDateString()
});
我需要从数据库中提取一些关于支持票的信息。每张票都与一个医学成像系统相关联,每个系统可能有也可能没有与之相关的服务范围。如果是,则可能有多个服务封面条目,但只有一个我们感兴趣。
我知道这不是有效的 Linq,但我真正想做的是以下...
var tickets = cxt.SupportTickets
.Select( t => new {
ID = t.ID,
Customer = t.Customer.Name,
var cover = t.System.CoverItems.FirstOrDefault(ci => // some query)
CoverLevel = cover?.Level.Name,
Expiry = cover?.Expiry.ToLongDateString()
});
有什么办法吗?我知道我可以为封面中想要的每一位数据重复 t.CoverItems.FirstOrDefault(...)
位,但是除了这会产生绝对糟糕的代码混乱之外,它会非常低效,因为它需要做同样的事情对每张票进行多次子查询。
我考虑过将其全部分解为一个 foreach
循环,但后来我看不出如何创建 tickets
集合。我不能创建一个空集合然后向其中添加对象,因为它们是匿名类型,我不想考虑您将如何指定泛型类型!
有人有什么想法吗?
您可以提高可读性:
var tickets = cxt.SupportTickets
.Select(t => new {
Ticket = t,
CoverItem = t.System.CoverItems.FirstOrDefault(ci => // some query)
})
.Select(x => new {
ID = x.Ticket.ID,
Customer = x.Ticket.Customer.Name,
CoverLevel = x.CoverItem?.Level.Name,
Expiry = x.CoverItem?.Expiry.ToLongDateString()
});
您可以使用查询符号来代替使用 let
子句:
var query=from t in cxt.SupportTickets
let cover = t.System.CoverItems.FirstOrDefault(ci => some query)
select new {
ID = t.ID,
Customer = t.Customer.Name,
CoverLevel = cover?.Level.Name,
Expiry = cover?.Expiry//.ToLongDateString()
};
最后将做与@TimSchmelter 回答相同的事情,但对于类似的事情,您可以使用 let
。另一件事,我几乎可以肯定 ToLongDateString()
方法在 EF 中不受支持。
这个我试过了(如果你想单独开发子查询,因为SoC原理):
var innerQuery = cxt.SupportTickets
.Where(artist => artist.coverId == SomeParameter)
.Select(artist => new {
artistId = artist.artistId,
artistCompleteName = artist.artistName,
artistMasterPiece = artist.CoverName
});
var tickets = cxt.SupportTickets
.Where(
t => innerQuery.Contains(t.coverId)
)
.Select( t => new {
ID = t.ID,
Customer = t.Customer.Name,
var cover = t.System.CoverItems.FirstOrDefault()
CoverLevel = cover?.Level.Name,
Expiry = cover?.Expiry.ToLongDateString()
});