Выражение запроса с предложением join и с предложением into, за которым следует предложение, отличное от select,
from x1 in e1 join x2 in e2 on k1 equals k2 into g
переводится в
from * in ( e1 ) . GroupJoin( e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => new { x1 , g })
Выражение запроса с продолжением orderby
from x in e orderby k1 , k2 , , kn
переводится в
from x in ( e ) . OrderBy ( x => k1 ) . ThenBy ( x => k2 ) . . ThenBy ( x => kn )
Если в предложении упорядочения указывается показатель направления descending, вместо него вызывается оператор OrderByDescending или ThenByDescending.
В следующих переводах предполагается, что в каждом выражении запроса нет предложений let, where, join и orderby, и есть не больше одного начального предложения from.
В примере
from c in customers from o in c.Orders select new { c.Name, o.OrderID, o.Total }
from c in customers from o in c.Orders orderby o.Total descending select new { c.Name, o.OrderID, o.Total }
переводится в
from * in customers. SelectMany(c => c.Orders, (c,o) => new { c, o }) orderby o.Total descending select new { c.Name, o.OrderID, o.Total }
конечный перевод имеет вид
customers. SelectMany(c => c.Orders, (c,o) => new { c, o }). OrderByDescending(x => x.o.Total). Select(x => new { x.c.Name, x.o.OrderID, x.o.Total })
где x идентификатор, созданный компилятором, который в других условиях является невидимым и недоступным.
В примере
from o in orders let t = o.Details.Sum(d => d.UnitPrice * d.Quantity) where t >= 1000 select new { o.OrderID, Total = t }
переводится в
from * in orders. Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) }) where t >= 1000 select new { o.OrderID, Total = t }
конечный перевод имеет вид
orders. Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) }). Where(x => x.t >= 1000). Select(x => new { x.o.OrderID, Total = x.t })
где x идентификатор, созданный компилятором, который в других условиях является невидимым и недоступным.
В примере
from c in customers join o in orders on c.CustomerID equals o.CustomerID select new { c.Name, o.OrderDate, o.Total }
переводится в
customers.Join(orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c.Name, o.OrderDate, o.Total })
В примере
from c in customers join o in orders on c.CustomerID equals o.CustomerID into co let n = co.Count() where n >= 10 select new { c.Name, OrderCount = n }
переводится в
from * in customers. GroupJoin(orders, c => c.CustomerID, o => o.CustomerID, (c, co) => new { c, co }) let n = co.Count() where n >= 10 select new { c.Name, OrderCount = n }
конечный перевод имеет вид
customers. GroupJoin(orders, c => c.CustomerID, o => o.CustomerID, (c, co) => new { c, co }). Select(x => new { x, n = x.co.Count() }). Where(y => y.n >= 10). Select(y => new { y.x.c.Name, OrderCount = y.n)
где x и y идентификаторы, созданные компилятором, которые в других условиях являются невидимыми и недоступными.
В примере
from o in orders orderby o.Customer.Name, o.Total descending select o
за исключением случая, когда v является идентификатором x, тогда перевод имеет вид просто
( e )
Например
from c in customers.Where(c => c.City == London) select c
переводится просто в
customers.Where(c => c.City == London)
Groupby
Выражение запроса вида
from x in e group v by k
переводится в
( e ) . GroupBy ( x => k , x => v )
за исключением случая, когда v является идентификатором x, тогда перевод имеет вид
( e ) . GroupBy ( x => k )
В примере
from c in customers group c.Name by c.Country
переводится в
customers. GroupBy(c => c.Country, c => c.Name)
7.16.2.7 Прозрачные идентификаторы
При некоторых переводах вставляются переменные диапазона с прозрачными идентификаторами, обозначенными *. Прозрачные идентификаторы не являются правильным языковым средством, они существуют только в виде промежуточного этапа в процессе перевода выражений запросов.
Когда при переводе запроса вставляется прозрачный идентификатор, дальнейшие этапы перевода распространяют прозрачный идентификатор в анонимные функции и анонимные инициализаторы объектов. В таких контекстах прозрачные идентификаторы функционируют следующим образом.
· Когда прозрачный идентификатор является параметром анонимной функции, члены связанного анонимного типа автоматически оказываются в области действия в теле анонимной функции.
· Когда член с прозрачным идентификатором находится в области действия, члены этого члена также находятся в области действия.
· Когда прозрачный идентификатор оказывается в роли декларатора члена в инициализаторе анонимного объекта, он создает член с прозрачным идентификатором.
На этапах перевода, описанных выше, прозрачные идентификаторы всегда создаются вместе с анонимными типами с намерением сохранить несколько переменных диапазона в виде членов одного объекта. В реализации C# для группирования нескольких переменных диапазона можно использовать механизм, отличный от анонимных типов. В следующих примерах перевода предполагается использование анонимных типов и показывается, как при переводе можно избавиться от прозрачных идентификаторов.
В примере
from c in customers from o in c.Orders orderby o.Total descending select new { c.Name, o.Total }
переводится в
from * in customers. SelectMany(c => c.Orders, (c,o) => new { c, o }) orderby o.Total descending select new { c.Name, o.Total }
и далее переводится в
customers. SelectMany(c => c.Orders, (c,o) => new { c, o }). OrderByDescending(* => o.Total). Select(* => new { c.Name, o.Total })
что после удаления прозрачных идентификаторов эквивалентно
customers. SelectMany(c => c.Orders, (c,o) => new { c, o }). OrderByDescending(x => x.o.Total). Select(x => new { x.c.Name, x.o.Total })
где x идентификатор, созданный компилятором, который в других условиях является невидимым и недоступным.
В примере
from c in customers join o in orders on c.CustomerID equals o.CustomerID join d in details on o.OrderID equals d.OrderID join p in products on d.ProductID equals p.ProductID select new { c.Name, o.OrderDate, p.ProductName }
переводится в
from * in customers. Join(orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c, o }) join d in details on o.OrderID equals d.OrderID join p in products on d.ProductID equals p.ProductID select new { c.Name, o.OrderDate, p.ProductName }
что дальше сокращается до
customers. Join(orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c, o }). Join(details, * => o.OrderID, d => d.OrderID, (*, d) => new { *, d }). Join(products, * => d.ProductID, p => p.ProductID, (*, p) => new { *, p }). Select(* => new { c.Name, o.OrderDate, p.ProductName })
конечный перевод имеет вид
customers. Join(orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c, o }). Join(details, x => x.o.OrderID, d => d.OrderID, (x, d) => new { x, d }). Join(products, y => y.d.ProductID, p => p.ProductID, (y, p) => new { y, p }). Select(z => new { z.y.x.c.Name, z.y.x.o.OrderDate, z.p.ProductName })
где x, y и z идентификаторы, созданные компилятором, которые в других условиях являются невидимыми и недоступными.