2010-11-27 6 views
7

Ich wechselte gerade von Linq 2 SQL zu Entity Framework, und ich sehe einige seltsame Verhaltensweisen in EF, ich hoffe, dass jemand helfen kann. Ich habe versucht herum zu googlen, aber ich konnte keine anderen Leute mit demselben Problem finden. Ich habe ein Szenario ausgeheckt, um die Situation zu erklären.Entity Framework und Repository Pattern (Problem mit IQueryable)

Wenn ich direkt mit einem EF-Kontext arbeite, kann ich eine Auswahl innerhalb einer Auswahl treffen. Beispielsweise führt dies völlig in Ordnung:

 // this is an Entity Framework context that inherits from ObjectContext 
     var dc = new MyContext(); 

     var companies1 = (from c in dc.Companies 
          select new { 
           Company = c, 
           UserCount = (from u in dc.CompanyUsers 
              where u.CompanyId == c.Id 
              select u).Count() 
          }).ToList(); 

Allerdings, wenn ich ein Repository-Muster verwenden, auf dem das Repository IQueryable (oder sogar ObjectSet oder Object) zurückkehrt, erhalte ich eine NotSupportedException (LINQ to Entities nicht das Verfahren erkennen ‚System.Linq.IQueryable`1) ...

Hier ein Beispiel für meine Repository ist:

public class Repository { 
    private MyContext _dc; 

    public Repository() { 
     _dc = new MyContext(); 
    } 

    public IQueryable<Company> GetCompanies() { 
     return _dc.Companies; 
    } 

    public IQueryable<CompanyUser> GetCompanyUsers() { 
     return _dc.CompanyUsers; 
    } 
} 

// ich bin das Repository innerhalb einer anderen Klasse (zB in meiner Services-Ebene)

 var repository = new Repository(); 

     var companies2 = (from c in repository.GetCompanies() 
          select new { 
           Company = c, 
           UserCount = (from u in repository.GetCompanyUsers() 
              where u.CompanyId == c.Id 
              select u).Count() 
          }).ToList(); 

Der obige Code löst eine NotSupportedException aus.

Ich weiß, dass, wenn es eine Verbindung zwischen Unternehmen und CompanyUsers, dann kann ich einfach das tun, und es wird funktionieren:

 var companies3 = (from c in repository.GetCompanies() 
          select new { 
           Company = c, 
           UserCount = (from u in c.CompanyUsers 
              select u).Count() 
          }).ToList(); 

... aber mein Beispiel ist nur eine vereinfachte Version eines komplizierteren Szenario, in dem ich keine Verbindung zwischen den Entitäten habe.

Also ich bin sehr verwirrt, warum Entity Framework die NotSupportedException wirft. Wie funktioniert es, dass die Abfrage einwandfrei funktioniert, wenn ich direkt mit dem EF-Kontext arbeite, aber nicht, wenn ich mit IQueryable arbeite, das von einer anderen Methode stammt? Dies funktionierte perfekt mit Linq 2 SQL, aber es scheint nicht in Entity Framework zu funktionieren.

Jeder Einblick würde sehr geschätzt werden.

Vielen Dank im Voraus.

Antwort

7

Ich vermute, dass, was passiert, ist, dass EF sieht den Ausdruck für repository.GetCompanyUsers() innerhalb des Lambda für die erste select und weiß nicht, was damit zu tun, weil repository ist kein EF-Kontext. Ich denke, wenn Sie das IQueryable direkt anstelle eines Ausdrucks übergeben, der es zurückgibt, sollte es funktionieren.

Wie wäre es, wenn Sie dies tun:

var companyUsers = repository.GetCompanyUsers(); 
    var companies2 = (from c in repository.GetCompanies() 
         select new { 
          Company = c, 
          UserCount = (from u in companyUsers 
             where u.CompanyId == c.Id 
             select u).Count() 
         }).ToList(); 
+0

Warum brauchen wir das? Ich meine, wir haben hier Assoziationen? – paragy

+0

+1 U beantwortet es auch .. – paragy

+0

Dies erfordert zwei Rundfahrten zum Server obwohl ... warten, oder? –

3

Dies ist einer jener seltsamen Macken mit Linq to SQL/EF. Anscheinend haben sie eine Möglichkeit implementiert, von einem Getter Property in SQL zu übersetzen, aber nicht eine Möglichkeit, von einem Getter Funktion zu SQL zu übersetzen. Wenn Sie anstelle einer Funktion GetCompanyUsers() eine Eigenschaft wie CompanyUsers verwenden, sollte es funktionieren.

Weird eh?

Also statt

public IQueryable<CompanyUser> GetCompanyUsers() { 
    return _dc.CompanyUsers; 
} 

Sie tun könnte

public IQueryable<CompanyUser> CompanyUsers { 
    get { return _dc.CompanyUsers; } 
} 

Soweit parametrisierte Abfragen gehen (die Sie nicht mit einer Eigenschaft tun, natürlich), siehe meine Frage beantwortet: Custom function in Entity Framework query sometimes translates properly, sometimes doesn't

Sie können auch wheres und selects in der Eigenschaft auch haben; sie werden gut übersetzen. wenn ich einen Blog mit einem Artikel Tisch hatte, dass enthält einige Artikel zum Beispiel, die nicht online sind:

public IQueryable<Article> LiveArticles { 
    get { return _dc.Articles.Where(a => !a.IsDraft); } 
} 

Das wird auch die Anzahl der parametrisierte Abfragen reduzieren, die Sie benötigen.

+0

Sehr komisch, aber du hast absolut recht. Eigenschaften, die IQueryable zurückgeben, arbeiten mit EF. Linq to SQL hat auch mit Methoden gearbeitet, aber ich denke, EF hat das nicht implementiert. –