2017-06-30 5 views
3

Ich versuche zu verstehen, warum diese Linq nicht kompiliert (fundInvoices nicht sichtbar sind):Multiple Linq „von“ und Variablen Sichtbarkeit

Dictionary<Fund, IEnumerable<Invoice>> paidfundInvoices; 
... 
from fundInvoices in paidfundInvoices 
from p in fundInvoices.Value 
group p by p.VendorId into ps 
select new Payment 
{ 
    FundId = fundInvoices.Key.FundId, // ERROR here 
    Value = ps.Sum(p => p.Amount) 
} 

Also ging ich auf diese anonymen Typ Nutzung zu ändern und fundInvoices hier auf magische Weise sichtbar ist:

from fundInvoices in paidfundInvoices 
select new 
{ 
    Fund = fundInvoices.Key, 
    Payments = from p in fundInvoices.Value 
       group p by p.VendorId into ps 
       select new Payment 
       { 
        FundId = fundInvoices.Key.FundId, // NO ERROR                                  
        Value = ps.Sum(p => p.Amount) 
       } 
}; 

Aber der anonyme Typ scheint überflüssig zu sein, ich mache keinen Gebrauch davon. Ich brauche nur eine flache Liste von Zahlung Objekte. Aber mein Code kompiliert nur so ...

Antwort

14

ich versuche zu verstehen, warum diese Linq nicht

nicht kompiliert

Der Schlüssel zum Verständnis ist, den Abschnitt der Spezifikation lesen auf wie Abfragen in normalen Code gesenkt werden.

Lassen Sie uns mit Ihrer Anfrage starten:

from fundInvoices in paidfundInvoices 
from p in fundInvoices.Value 
group p by p.VendorId into ps 
select new Payment { 
    FundId = fundInvoices.Key.FundId, // ERROR here 
    Value = ps.Sum(p => p.Amount) 
} 

OK, Schritt eins. Die Regel in der Spezifikation ist:

Eine Abfrage-Ausdruck mit einer Fortsetzung wird from … into x … in from x in (from …) …

übersetzt

Ihre Anfrage ist jetzt

from ps in (
    from fundInvoices in paidfundInvoices 
    from p in fundInvoices.Value 
    group p by p.VendorId) 
select new Payment { 
    FundId = fundInvoices.Key.FundId, // ERROR here 
    Value = ps.Sum(p => p.Amount) 
} 

Und jetzt sollte klar sein, warum fundInvoices ist nicht in der Auswahlklausel enthalten. fundInvoices ist eine Bereichsvariable einer völlig anderen Abfrage.

Aber falls das nicht klar ist, lass uns weitermachen. Die nächste Regel ist:

Eine Abfrage Ausdruck des Formulars wird from x in e select v in (e) . Select (x => v)

übersetzt

Ihre Anfrage ist jetzt

((from fundInvoices in paidfundInvoices 
    from p in fundInvoices.Value 
    group p by p.VendorId)) 
    .Select(ps => 
    new Payment { 
     FundId = fundInvoices.Key.FundId, 
     Value = ps.Sum(p => p.Amount) 
    }) 

Jetzt können wir die innere Abfrage übersetzen:

Ein Abfrageausdruck mit einer zweiten from-Klausel gefolgt von s omething andere als eine select-Klausel wird from x1 in e1 from x2 in e2 … in from * in (e1) . SelectMany(x1 => e2 , (x1 , x2) => new { x1 , x2 }) …

Die eine „transparente Kennung“ * ist übersetzt und wir werden sehen, was es in einer Minute bedeutet.

Ihre Anfrage ist jetzt

((from * in (paidfundInvoices).SelectMany(
    fundInvoices => fundInvoices.Value, 
    (fundInvoices, p) => new {fundInvoices, p}) 
    group p by p.VendorId)) 
    .Select(ps => 
    new Payment { 
     FundId = fundInvoices.Key.FundId, 
     Value = ps.Sum(p => p.Amount) 
    }) 

letzte Regel:

Eine Abfrage Ausdruck der Form from x in e group v by k in (e) . GroupBy (x => k , x => v)

übersetzt

Also das ist

((((paidfundInvoices).SelectMany(
    fundInvoices => fundInvoices.Value, 
    (fundInvoices, p) => new {fundInvoices, p})) 
    .GroupBy(* => p.VendorId, * => p))) 
    .Select(ps => 
    new Payment { 
     FundId = fundInvoices.Key.FundId, 
     Value = ps.Sum(p => p.Amount) 
    }) 

Die * bedeutet "Bring das Paar von Mitgliedern des anonymen Typs in der Select-Many in den Geltungsbereich. Desugar dass und entfernen Sie die unnötigen Pars und wir haben die endgültige Form der Abfrage:

paidfundInvoices 
    .SelectMany(
    fundInvoices => fundInvoices.Value, 
    (fundInvoices, p) => new {fundInvoices, p}) 
    .GroupBy(pair => pair.p.VendorId, pair => pair.p))) 
    .Select(ps => 
    new Payment { 
     FundId = fundInvoices.Key.FundId, 
     Value = ps.Sum(p => p.Amount) 
    }) 

Und jetzt sollte es sehr klar sein, warum fundInvoices in der Fortsetzung in ihrem Umfang nicht ist. Es wäre im Bereich in der GroupBy dank transparenter Bezeichner desugaring, aber es ist überhaupt nicht im Bereich in der Select.

Allgemeiner: in LINQ Tive von Erklärungen auf der linken Seite zu Verwendungen auf der rechten Seite fließen im Allgemeinen, aber es gibt einige Ausnahmen: ein into Bereichsvariablen von Umfang entfernt, sind nicht alle Bereichsvariablen an allen Stellen in ihrem Umfang ein join Klausel und so weiter. Lesen Sie die Spezifikation für weitere Details.

1

Sobald Sie die groupinto tun, können Sie nicht mehr auf die ursprünglichen from Variablen zugreifen. Wenn Sie den Zugriff benötigen, setzen Sie es in der Gruppe:

from fundInvoices in paidfundInvoices          
from p in fundInvoices.Value 
group new { fundInvoices, p } by p.VendorId into ps 
     select new Payment 
       { 
        FundId = ps.fundInvoices.FundId, 
        Value = ps.Sum(p => p.Amount) 
       }