2017-05-09 206 views
1

我有一種情況在下面的嵌套C#LINQ嵌套select查詢

--Orders (List) 
----Products (List) 
------Manufacturers (List) 
     FIELDS 
     -Name 
     -Address 
     -City 

在這種情況下,我需要執行查詢,這將在過濾的製造商並返回Orders, Products & only matching city manufacturers

我試圖把以下查詢,但我收到所有產品列表即使城市不匹配製造商

var filteredOrders = from o in Orders 
        from t in o.Products       
        where t.Manufacturers.Any(v => v.City == "Hartford") 
        select o; 

或者,即使我從select o更改爲「選擇t.Manufacturers」我得到製造商的所有名單,不論城市濾波器。

幸運的是,我得到了與我的場景匹配的W3school SQL示例。 https://www.w3schools.com/sql/trysql.asp?filename=trysql_op_or

SQL查詢:

SELECT o.OrderId, p.ProductName, s.* 
FROM [Orders] o 
JOIN OrderDetails od ON o.OrderId = od.OrderId AND o.orderId = 10248 
JOIN Products p ON od.ProductId = p.ProductId 
JOIN Suppliers s ON p.SupplierId = s.SupplierId and s.City ='Singapore'  
+0

是什麼'p.Products'的'p'? – shole

+0

@shole,我的壞,編輯! – Kenz

+0

'Any()'返回true或false,所以如果任何城市是「Hartford」,它將返回true,意味着你什麼都不過濾。嘗試最簡單的確切條款:'哪裏t.Manufacturers.City ==「哈特福德」' – shole

回答

0

我終於嘗試將所有東西放在一起,並獲得了預期的輸出。

var fp = orders.Select(o => 
      { 
       o.products = o.products.Select(p => 
       { 
        p.manufacturers.RemoveAll(m => m.City != "Hartford"); 
        return p; 
       }).ToList(); 

       return o; 
      }); 

請建議,如果任何人有更好的解決方案

2

我會拉平一切,然後只在城市篩選您希望:

class Manufacturer 
{ 
    public string Name; 
    public string Address; 
    public string City; 
} 

class Product 
{ 
    public Manufacturer[] Manufacturers; 
} 

class Order 
{ 
    public Product[] Products; 
} 

static void Main(string[] args) 
{ 
    var cities = new string[] { "a", "b" }; 
    Order[] orders = null; 
    orders.SelectMany(o => o.Products.SelectMany(p => p.Manufacturers.Select(m => new { o, p, m }))) 
     .Where(g => cities.Contains(g.m.City)) 
     .ToList(); 
    } 

另外,如果你想返回Order小號(因爲它們有不同的Products,它必須指向新分配的Object )你可以改爲:

var newOrders = orders.Select(o => new Order() 
{ 
    Products = o.Products 
    .Select(p => new Product() 
    { 
     Manufacturers = p.Manufacturers.Where(m => cities.Contains(m.City)).ToArray() 
    }) 
    .Where(m => m.Manufacturers.Length > 0).ToArray() 
}).Where(p => p.Products.Length > 0).ToArray(); 
+0

我部分同意你的建議。但是,我不想創建新對象。我想只提取那些來自匹配城市的製造商的產品訂單。我的意思是,如果我將它與SQL語句相關聯,我可以輕鬆獲得匹配的記錄。 同樣在你的建議中,如果我展開「o」和「p」,那麼它們與默認列表(未過濾)相同。 – Kenz

+1

@AviKenjale我編輯了這篇文章。你不能指望'Order'對象因爲過濾而改變。您必須「刪除」項目或創建一個新項目。這有點像'byref' vs'byval' – tafia

0

你正在申請城市過濾器錯誤。這是這條線。

where t.Manufacturers.Any(v => v.City == "Hartford") 

Any回報true,廠家至少一個具有城市性質爲「哈特福德」所以基本上你的查詢是這樣的

var filteredOrders = from o in Orders 
       from t in o.Products       
       where true//←This is the problem 
       select o; 

你需要做的其實

where t.Manufacturers.City == "Hartford" 

我希望這有助於

E xample:

var cityNames = new List<string> {"New York", 
            "Atlanta", 
            "Hartford", 
            "Chicago" 
            }; 
var anyResult = cityNames.Any(x=>x== "Hartford"); //TRUE 
var whereResult = cityNames.Where(x => x == "Hartford"); //IEnumerable<string>, in this case only one element 
+0

@AviKenjale,不幸的是你錯了。不返回布爾的地方。它的作用是根據你提供的謂詞過濾元素,在這種情況下,t.Manufacturers.City ==「Hartford」 –

+0

我的意思是,Where返回匹配bool條件的集合。 – Kenz

+0

@ AviKenjale,是的,而「任何」都不是。如果至少有一個元素匹配條件,請參閱我的答案以更好地理解或使用它,對於有效的代碼,返回true或false是什麼意思 –

0

我想不出一種可以完全避免創建新對象的方式,因爲父對象的列表屬性不能直接過濾。儘管如此,你可以使用同一個類。

此外,我使用兩個單獨的查詢,以便在父/祖父對象中創建一個新列表。

我做了一個小的演示驗證這個想法(以下具有同等代碼): http://ideone.com/MO6M6t

我儘量選擇城市是"tmp"只在父p3,只屬於盛大母公司g1g3

預期輸出是:

g1 
    p3 
     tmp 

g3 
    p3 
     tmp 
using System; 
using System.Collections.Generic; 
using System.Linq; 

public class Test 
{ 
    public class GrandParent{ 
     public List<Parent> parentList{ get; set; } 
     public string name{ get; set; } 
     public GrandParent(string name){ 
      this.name = name; 
      this.parentList = new List<Parent>(); 
     } 
    } 
    public class Parent{ 
     public List<Child> childList{ get; set;} 
     public string name{ get; set; } 
     public Parent(string name){ 
      this.name = name; 
      this.childList = new List<Child>(); 
     } 
    } 
    public class Child{ 
     public string city{ get; set;} 
     public Child(string city){ 
      this.city = city; 
     } 
    } 
    public static void Main() 
    { 
     Child c1 = new Child("ABC"), c2 = new Child("123"), c3 = new Child("tmp"); 
     Parent p1 = new Parent("p1"), p2 = new Parent("p2"), p3 = new Parent("p3"); 
     GrandParent g1 = new GrandParent("g1"), g2 = new GrandParent("g2"), g3 = new GrandParent("g3"); 

     p1.childList.Add(c1); p1.childList.Add(c2); 
     p2.childList.Add(c2); 
     p3.childList.Add(c3); 

     g1.parentList.Add(p1); g1.parentList.Add(p2); g1.parentList.Add(p3); 
     g2.parentList.Add(p2); 
     g3.parentList.Add(p3); 

     List<GrandParent> repo = new List<GrandParent>{g1, g2, g3}; 

     var filteredParents = from g in repo 
           from p in g.parentList 
           where p.childList.Any(c => c.city == "tmp") 
           select new Parent(p.name){ 
           childList = p.childList.Where(c => c.city == "tmp").ToList() 
           }; 

     var filteredGrandParents = from g in repo 
            from p in g.parentList 
            where filteredParents.Any(fp => fp.name == p.name) 
            select new GrandParent(g.name){ 
             parentList = g.parentList.Where(pp => filteredParents.Any(fp => fp.name == pp.name)).ToList() 
            }; 

     foreach(var g in filteredGrandParents){ 
      Console.WriteLine(g.name); 
      foreach(var p in g.parentList){ 
       Console.WriteLine("\t" + p.name); 
       foreach(var c in p.childList){ 
        Console.WriteLine("\t\t" + c.city); 
       } 
      } 
      Console.WriteLine(); 
     } 
    } 
}