2017-06-30 31 views
3

我試圖理解爲什麼這個LINQ不會編譯(fundInvoices不可見):多LINQ「從」和變量的知名度

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) 
} 

於是我去到這個改變匿名類型的使用和fundInvoices是神奇可見這裏:

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) 
       } 
}; 

但是,匿名類型似乎是多餘的,我不作任何利用這一點。我只需要一個支付對象的平面清單。然而,我的代碼只能編譯這種方式...

回答

14

我試圖理解爲什麼這個LINQ不會編譯

來理解的關鍵是閱讀說明書的部分查詢如何降低到正常的代碼。

讓我們先從您的查詢:

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,一步一個。在規範的規則是:

與延續from … into x …查詢表達式轉換成from x in (from …) …

您的查詢已經

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) 
} 

現在應該很清楚爲什麼fundInvoices是不在select子句的範圍內。 fundInvoices是完全不同的查詢的範圍變量

但是,如果不清楚,讓我們繼續前進。下一條規則是:

形式from x in e select v的查詢表達式轉換成(e) . Select (x => v)

您的查詢已經

((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) 
    }) 

現在,我們可以轉換內部查詢:

查詢表達式的第二個from子句後跟s omething比SELECT子句from x1 in e1 from x2 in e2 …其他被翻譯成from * in (e1) . SelectMany(x1 => e2 , (x1 , x2) => new { x1 , x2 }) …

*是「透明的標識符」,我們將看到這意味着什麼在一分鐘內。

您的查詢現在

((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) 
    }) 

決賽規則:

形式from x in e group v by k的查詢表達式轉換成(e) . GroupBy (x => k , x => v)

所以這是

((((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) 
    }) 

*的意思是「將select-many中選定的匿名類型的成員變爲範圍。 Desugar這一點,刪除不必要的括號,我們有查詢的最終形式:

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) 
    }) 

現在應該很清楚爲什麼fundInvoices不是在延續範圍。它GroupBy範圍內由於透明標識符desugaring,但它根本不在Select範圍內。

更普遍的:在LINQ範圍一般從申報流程就留給用途的權利,但也有一些例外:一個into去除作用域範圍變量,不是所有的範圍變量的範圍在一join所有地方條款等等。閱讀規範瞭解更多細節。

1

一旦你做了groupinto你不能再訪問原來的from變量。如果您需要訪問,把它放在該組中:

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) 
       }