2013-02-12 119 views
3

我有以下類主/細節:LINQ查詢扁平化與子細節

public class Order 
{ 
    public Order() 
    { 
     Items = new ObservableCollection<OrderItem>(); 
    } 

    public string Code { get; set; } 
    public ICollection<OrderItem> Items { get; set; } 
} 

public class OrderItem 
{ 
    public OrderItem() 
    { 
     SubItems = new ObservableCollection<SubItem>(); 
    } 

    public decimal Quantity { get; set; } 
    public decimal Price { get; set; } 
    public ICollection<SubItem> SubItems { get; set; }   
} 

public class SubItem 
{ 
    public DateTime Date { get; set; } 
    public decimal Quantity { get; set; } 
    public string UserName { get; set; } 
} 

現在我的問題是,我想顯示在一個DataGrid,如這些類的所有數據。使用代碼:

Order order = new Order(); 
     order.Code = "123"; 
     order.Items.Add(new OrderItem() 
           { 
            Price = 30, 
            Quantity = 3, 
            SubItems = new Collection<SubItem>() 
             { 
              new SubItem() { Date = DateTime.Now, Quantity = 1, UserName = "User1" }, 
              new SubItem() { Date = DateTime.Now, Quantity = 2, UserName = "User2" } 
             } 
           }); 


     order.Items.Add(new OrderItem() 
           { 
            Price = 500, 
            Quantity = 50, 
            SubItems = new Collection<SubItem>() 
             { 
              new SubItem() { Date = DateTime.Now, Quantity = 20, UserName = "User1" }, 
              new SubItem() { Date = DateTime.Now, Quantity = 20, UserName = "User2" }, 
              new SubItem() { Date = DateTime.Now, Quantity = 10, UserName = "User3" } 
             } 
           }); 

我需要顯示在一個DataGrid是這樣的:

| Order.Code | Item.Price | Item.Quantity | SubItem.Quantity | SubItem.UserName | 
| 123  | 30   | 3    | 1    | User1   | 
| 123  | 30   | 3    | 2    | User2   | 
| 123  | 500  | 50   | 20    | User1   | 
| 123  | 500  | 50   | 20    | User2   | 
| 123  | 500  | 50   | 10    | User3   | 

看起來很簡單,但我不能這樣做。我能做的最好的方法是在SubItem上加上OrderItem的引用,這樣我就可以在列數據綁定上使用它,但只有當我有子項時(如果沒有SubItems,我仍然需要顯示OrderItem數據)才能使用它。所以基本上我需要顯示我看到的確切的東西,如果我執行一個SQL SELECT加入3個表。

任何linq魔術都可以做到這一點?

回答

4

試試這個,它就像一個加盟:

var table = from item in order.Items 
      from subItem in item.SubItems 
      select new 
       { 
        OrderCode = order.Code, 
        ItemPrice = item.Price, 
        ItemQuantity = item.Quantity, 
        SubItemQuantity = subItem.Quantity, 
        SubItemUserName = subItem.UserName 
       }; 

,如果你喜歡,你可以將此擴展到多個訂單:

var table = from order in orders 
      from item in order.Items 
      from subItem in item.SubItems 
      select new 
       { 
        OrderCode = order.Code, 
        ItemPrice = item.Price, 
        ItemQuantity = item.Quantity, 
        SubItemQuantity = subItem.Quantity, 
        SubItemUserName = subItem.UserName 
       }; 

這裏是一個鏈接:http://msdn.microsoft.com/en-us/library/bb383978.aspx(請參見FROM子句複合)

如果集合可以爲空,則使用DefaultIfEmpty(),例如:

var table = from order in orders 
      from item in order.Items.DefaultIfEmpty(new OrderItem()) 
      from subItem in item.SubItems.DefaultIfEmpty(new SubItem()) 
      select new 
       { 
        OrderCode = order.Code, 
        ItemPrice = item.Price, 
        ItemQuantity = item.Quantity, 
        SubItemQuantity = subItem.Quantity, 
        SubItemUserName = subItem.UserName 
       }; 

如果你沒有在參數DefaultIfEmpty傳()「默認」的項目將是空的,所以你必須應付的是,如:

var table = from order in orders 
      from item in order.Items.DefaultIfEmpty() 
      from subItem in (item != null ? item.SubItems : Enumerable.Empty<SubItem>()).DefaultIfEmpty() 
      select new 
       { 
        OrderCode = order.Code, 
        ItemPrice = item != null ? item.Price.ToString() : "n/a", 
        ItemQuantity = item != null ? item.Quantity.ToString() : "n/a", 
        SubItemQuantity = subItem != null ? subItem.Quantity.ToString() : "n/a", 
        SubItemUserName = subItem != null ? subItem.UserName : "n/a" 
       }; 

另一種選擇:

var table = from order in orders 
      from item in order.Items.DefaultIfEmpty() 
      from subItem in (item != null ? item.SubItems : Enumerable.Empty<SubItem>()).DefaultIfEmpty() 
      select new 
       { 
        OrderCode = order.Code, 
        ItemPrice = item != null ? item.Price : default(decimal?), 
        ItemQuantity = item != null ? item.Quantity : default(decimal?), 
        SubItemQuantity = subItem != null ? subItem.Quantity : default(decimal?), 
        SubItemUserName = subItem != null ? subItem.UserName : null 
       }; 
+0

這看起來不錯,但如果OrderItem沒有子項目,它不會出現在結果上。我能夠使用'Concat'和另一個沒有'from subItem ...'和'where!item.SubItems.Any()'的查詢來做到這一點,但它看起來不很優雅:( – adanlif 2013-02-12 12:33:37

+0

我已經更新了我的答案包括任何子集合爲空的情況 – 2013-02-12 12:47:14

+0

真棒,謝謝! – adanlif 2013-02-12 13:49:24

5

壓扁使用SelectMany方法:

var res = order.Items 
       .SelectMany(i => i.SubItems, (Item, Sub) => new { Item, Sub }) 
       .Select(r => new { order.Code, 
            r.Item.Price, 
            ItemQuantity = r.Item.Quantity, 
            SubItemQuantity = r.Sub.Quantity, 
            r.Sub.UserName }); 

如果SubItems集合可能是空的,你可以使用DefaultIfEmpty方法:在Ideone

var res = order.Items 
       .SelectMany(i => i.SubItems.DefaultIfEmpty(), (Item, Sub) => new { Item, Sub }) 
       .Select(r => new { order.Code, 
            r.Item.Price, 
            ItemQuantity = r.Item.Quantity, 
            SubItemQuantity = r.Sub == null ? null : (Decimal?) r.Sub.Quantity , 
            UserName = r.Sub == null ? (string)null : r.Sub.UserName }); 

嘗試。

+0

感謝您的答案,但作爲安德烈洛克的答案,它不顯示'OrderItem'數據,如果沒有'SubItems' ...(PS。 :我不知道Ideone,非常好的工具) – adanlif 2013-02-12 12:37:03

+0

@ user1576950:我已經更新了答案和示例,以滿足您的需求。 – mipe34 2013-02-12 13:08:50