2017-05-09 2 views
1

Ich habe ein Szenario, in folgenden verschachteltenC# LINQ verschachtelte SELECT-Abfrage

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

In diesem Szenario würde ich brauche Abfrage auszuführen, die auf Stadt von Hersteller und gibt Orders, Products & only matching city manufacturers

filtert Ich habe versucht, folgende Abfrage zu stellen, aber ich bekomme alle Liste von Produkte, obwohl Stadt nicht zu Herstellern passt.

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

Oder auch wenn ich von select o zu ‚wählen t.Manufacturers‘ ändern Ich bin alle Liste der Hersteller immer unabhängig von Stadt Filter.

Zum Glück habe ich W3school SQL-Beispiel, das zu meinem Szenario passt. https://www.w3schools.com/sql/trysql.asp?filename=trysql_op_or

SQL Query:

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

was hat, ist 'P' von' p.Products'? – shole

+0

@shole, mein schlechtes, bearbeitet! – Kenz

+0

'Any()' return true oder false, also wenn einer der Stadt ist "Hartford", es gibt wahr, bedeutet, dass Sie nichts filtern. Versuchen Sie am einfachsten exakten Where-Klausel: 'Wo t.Hersteller.Stadt ==" Hartford "' – shole

Antwort

0

Ich habe schließlich versucht, alles zusammenzusetzen und die erwartete Ausgabe zu bekommen.

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

       return o; 
      }); 

Bitte vorschlagen, wenn jemand eine bessere Lösung

2

Ich würde alles abflachen und dann filtern, um nur auf Städte, die Sie wollen:

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

Alternativ, wenn Sie neueOrder s zurückkehren möchten (da sie eine andere Products haben, muss sie auf eine neu zugewiesene Object zeigen) Sie könnten dies stattdessen haben:

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

Ich stimme Ihrem Vorschlag teilweise zu. Ich möchte jedoch kein neues Objekt erstellen. Ich möchte nur die Bestellungen abholen, die Produkte von diesen Herstellern aus passenden Städten haben. Ich meine, wenn ich es mit SQL-Anweisungen in Verbindung bringe, kann ich leicht übereinstimmende Datensätze bekommen. Auch in Ihrem Vorschlag, wenn ich 'o' & 'p' erweitern, sind diese wie von der Standardliste (nicht gefiltert). – Kenz

+1

@AviKenjale Ich habe den Beitrag bearbeitet. Sie können nicht erwarten, dass sich das Objekt 'Order' ändert, weil Sie es filtern. Sie müssen entweder Elemente "entfernen" oder ein neues erstellen. Das ist irgendwie wie 'byref' vs.' byval' – tafia

0

Sie wenden Ihren City-Filter falsch an. Es ist diese Linie.

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

Any Rückkehr true, mindestens einer der Hersteller hat-Stadt als „Hartford“ so dass im Grunde Ihre Frage so etwas wie diese

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

in der Tat, was Sie tun müssen,

ist
where t.Manufacturers.City == "Hartford" 

Ich hoffe, das hilft

E Beispiel:

+0

@ AviKenjale, leider liegen Sie falsch. Wo gibt kein Bool zurück. Was es tut, ist Elemente herauszufiltern, die auf dem Prädikat basieren, das Sie liefern, in diesem Fall wo t.Hersteller.Stadt == "Hartford" –

+0

Ich meine, wo Rückholansammlung für zusammenpassenden bool Zustand. – Kenz

+0

@ AviKenjale, Ja genau während 'Any' das nicht tut. Was 'any' ist, gibt wahr oder falsch zurück, wenn mindestens ein Element die Bedingung erfüllt, siehe meine Antwort für ein besseres Verständnis oder benutze es so wie es ist, für einen funktionierenden Code –

0

Ich kann nicht an eine Möglichkeit denken, die das Erstellen neuer Objekte vollständig vermeiden kann, da die Listeneigenschaft des übergeordneten Objekts nicht direkt gefiltert werden kann. Sie können jedoch dieselbe Klasse verwenden.

Auch ich benutze zwei separate Abfragen, um eine neue Liste im Eltern/Großeltern-Objekt zu erstellen.

ich eine kleine Demo gemacht habe, die Idee zu demonstrieren (unter gleichwertigem Code hat): http://ideone.com/MO6M6t

Die Stadt versuche ich "tmp" zu wählen ist, die nur unter Eltern p3, die g1, zu groß Eltern gehören nur g3

die erwartete Ausgabe ist:

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