2017-05-09 207 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(); 
     } 
    } 
}