Вы можете написать этот конкретный запрос в LINQ следующим образом - хотя это приведет к коррелированному подзапросу:
var query =
from c in ctx.Customer
select new
{
Customer = c,
LatestInvoice = ctx.Invoice
.Where(i => i.CustomerId == c.CustomerId)
.OrderByDescending(i => i.InvoiceId)
.FirstOrDefault();
};
Если вы хотите сделать это по-другому, синтаксис LINQ менее читабелен, но вы можете немного разделить запрос благодаря отложенному выполнению:
var latestInvoicesPerCustomerQuery =
from inv in ctx.Invoice
group inv by inv.CustomerId into g
select new { CustomerId = g.Key, InvoiceId = g.Max(inv => inv.InvoiceId) };
var customersAndLatestInvoicesQuery =
from customer in ctx.Customer
join linv in latestInvoicesPerCustomer
on customer.CustomerId equals linv.CustomerId
into latestInvoiceJoin
from latestInvoice in latestInvoiceJoin.DefaultIfEmpty() // left join
join invoice in ctx.Invoice
on latestInvoice.InvoiceId equals invoice.InvoiceId
select new
{
Customer = customer,
LatestInvoice = invoice
};
Первый запрос (latestInvoicesPerCustomerQuery
) не будет выполнен, пока вы не выполните перечисление по нему или по второму запросу, который ссылается на первый. Что касается времени выполнения, то конечный запрос представляет собой одно дерево выражений, так что вы можете думать о первом запросе как о поглощенном во втором.
Если вы действительно хотите все это в одном запросе, вы можете сделать это тоже:
var customersAndLatestInvoicesQuery =
from customer in ctx.Customer
join linv in (
from inv in ctx.Invoice
group inv by inv.CustomerId into g
select new
{
CustomerId = g.Key,
InvoiceId = g.Max(inv => inv.InvoiceId)
}
)
on customer.CustomerId equals linv.CustomerId
into latestInvoiceJoin
from latestInvoice in latestInvoiceJoin.DefaultIfEmpty() // left join
join invoice in ctx.Invoice
on latestInvoice.InvoiceId equals invoice.InvoiceId
select new
{
Customer = customer,
LatestInvoice = invoice
};
Любой вариант customersAndLatestInvoicesQuery
должен примерно переводиться в SQL, который вы перечислите в своем посте.