如何编写需要子查询的 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()
  });