2016-04-07 6 views
1

Es ist eine Struktur:Entity Framework - wo clausule mit Any() und Alle() verwenden

-Client haben mehrere Fälle und Fall haben mehrere Protokolle.

public class Client 
    { 
     public int Id { get; set; } 
     public IEnumerable<Case> Cases { get; set; } 
    } 

    public class Case 
    { 
     public int CaseId { get; set; } 
     public IEnumerable<Log> Histories { get; set; } 
    } 

    public class Log 
    { 
     public int Id { get; set; } 
     public string Code { get; set; } 
    } 

Ich möchte Kunden abrufen, die alle Protokolle von jeweils Code-Eigenschaft auf ‚FALSCH‘ OR Clients haben, die überhaupt keine Protokolle haben. Außerdem habe ich paar einfache Bedingungen, die Sie unten vereinfacht für die Zwecke des Veröffentlichungscodes sehen können (natürlich EF verwendet IQueryable anstelle von ICollection).

Zuerst habe ich versucht, die Erweiterungsmethode AllOrEmpty zu erstellen, die funktioniert, aber nicht in Linq zu Entitäten (es akzeptiert keine Erweiterungsmethoden).

public static class Extensions 
    { 
     public static bool AllOrEmpty<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 
     { 
      return source.All(predicate) || !source.Any(); 
     } 
    } 

     var sampleIds = new List<int>() { 1, 2, 3 }; 

     Entities db = new Entities(); 
     db.Clients 
      .Where(client => client.Cases 
       .Where(cas => sampleIds.Contains(cas.CaseId)) 
       .SelectMany(cas => cas.Histories 
        .Where(log => log.Id < 10) 
        ) 
       .AllOrEmpty(log => log.Code == "WRONG") << ideal solution 
       ) 
       .Select(client => client.Id); 

Zweitens versuche ich Lambda-Ausdruck mit return-Anweisung in dem clausule und es funktioniert gut, aber nicht für IQueryable und gibt Fehler zu erzeugen: Ein Lambda-Ausdruck mit einer Aussage Körper

zu einem Ausdrucksbaum kann nicht konvertiert
  db.Clients 
      .Where(client => 
      { 
       var logs = client.Cases 
        .Where(cas => sampleIds.Contains(cas.CaseId)) 
        .SelectMany(cas => cas.Histories 
         .Where(log => log.Id < 10) 
         ); 

       return !logs.Any() || logs.All(log => log.Code == "WRONG"); 
      }) 
      .Select(client => client.Id); 

Ich habe keine Ahnung, wie man solche Abfrage erstellen und es einfach halten und etwas schmutzigen Code vermeiden. Irgendeine Idee?

+1

Mögliche Duplikat von [Entity Framework Filter "Ausdruck >"] (http://stackoverflow.com/questions/18337692/entity-framework-filter-expressionfunct-bool) – Igor

Antwort

0

sollte diese Linq-Abfrage tun, was Sie versuchen zu tun:

var IDs = from client in db.Clients 
       from cas in client.Cases.Where(c => sampleIds.Contains(c.CaseId)) 
       let logs = cas.Histories.Where(l => l.Id < 10) 
       where !logs.Any() || logs.All(l => l.Code == "WRONG") 
       select client.Id; 
0

Wenn Sie Kunden, die Fälle wollen, wo alle Geschichte Protokollelemente der Code auf „FALSCH“

var clientsWithWrongLogCode = clist.Where(s => s.Cases 
             .Any(c => c.Histories.All(h => h.Code == "WRONG"))); 
hat

Wenn Sie für alle Fälle alle Clients abrufen möchten, die kein Ereignisprotokollprotokoll haben.

var clientsWithNoLogs = clist.Where(s => s.Cases.Any(c => !c.Histories.Any())); 

Wenn Sie beide Bedingungen zusammen wollen.

var combined = clist.Where(s => s.Cases.Any(c => c.Histories.All(h => h.Code == "WRONG") 
                    || !c.Histories.Any())); 
+0

Sowohl Sie @Shyju und Josh Knack haben mich nicht verstanden .Ich möchte keine Fälle auswählen, die einen Zustand nach dem anderen erfüllen, aber ich behandle alle Protokolle von jedem Fall wie in einem Beutel und nur, wenn alle Protokolle von jedem Fall die Bedingungen erfüllen (alle haben den Code "FALSCH" oder dieser Beutel ist leer)) Ich möchte diesen Benutzer auswählen. – Lukas

+0

Versuchen Sie die letzte LINQ-Anweisung (kombiniert). – Shyju

1

Sie können die LINQ-Abfragesyntax nutzen, die mit let Klausel und transparente Identifikatoren stark solche Abfragen vereinfacht.

Zum Beispiel könnte Ihre Abfrage wie folgt sein:

var query = 
    from client in db.Clients 
    let logs = from cas in client.Cases 
       where sampleIds.Contains(cas.CaseId) 
       from log in cas.Histories 
       where log.Id < 10 
       select log 
    where !logs.Any() || logs.All(log => log.Code == "WRONG") 
    select client.Id; 

Aber ich möchte etwas in Bezug auf Ihre Erweiterungsmethode erwähnen.

Die Bedingung source.All(predicate) || !source.Any() (daher Ihre AllOrEmpty Methode) keinen Sinn machen, weil es auf beiden source.All(predicate) äquivalent ist (ja, dies ist kein Fehler) oder !source.Any(predicate).

Sie leicht, dass für LINQ to Entities, indem man die generierten SQL-Abfrage (ein und dasselbe) und für LINQ to Objects, indem man die Enumerable.Anyreference source oder die folgenden einfachen Test überprüfen können:

class Foo 
{ 
    public int Bar { get; set; } 
} 

var source = new List<Foo>(); 
bool test1 = !source.Any() || source.All(e => e.Bar == 0); 
bool test2 = source.All(e => e.Bar == 0); 
bool test3 = !source.Any(e => e.Bar == 0); 
Debug.Assert(test1 == test2 && test2 == test3); 
+0

Es ist lustig, aber Sie haben Recht, All() -Methode gibt True für leere Sammlung zurück, so löst es mein Problem vollständig. Vielen Dank! – Lukas