11

Ich versuche, das beste Muster für den Datenzugriff in meiner MVC-Anwendung zu entscheiden. Derzeit habe ich die MVC-Storefront-Serie verfolgt und verwende Repositories, die IQueryable einer Service-Schicht aussetzen, die dann Filter anwendet. Anfangs verwende ich LINQtoSQL, z.Sollten Repositorys IQQueryable der Service-Schicht aussetzen oder in der Implementierung filtern?

public interface IMyRepository 
{ 
    IQueryable<MyClass> GetAll(); 
} 

in Implementiert:

public class LINQtoSQLRepository : IMyRepository 
{ 
    public IQueryable<MyClass> GetAll() 
    { 
     return from table in dbContext.table 
      select new MyClass 
      { 
       Field1 = table.field1, 
       ... etc. 
      } 
    } 
} 

Filter für IDs:

public static class TableFilters 
{  
    public static MyClass WithID(this IQueryable<MyClass> qry, string id) 
    { 
     return (from t in qry 
       where t.ID == id 
       select t).SingleOrDefault(); 
    }  
} 

aus dem Dienst aufgerufen:

public class TableService 
{ 
    public MyClass RecordsByID(string id) 
    { 
     return _repository.GetAll() 
         .WithID(id); 
    } 
} 

ich in ein Problem lief, als ich mit der Umsetzung der experimentierten Repository mit Entity Framew Ork mit LINQ zu Entitäten. Die Filterklasse in meinem Projekt enthält einige komplexere Operationen als das "WHERE ... == ..." im obigen Beispiel, von dem ich glaube, dass je nach LINQ-Provider unterschiedliche Implementierungen erforderlich sind. Insbesondere muss ich eine SQL-Klausel "WHERE ... IN ..." ausführen. Ich bin in der Lage dies in der Filterklasse zu implementieren mit:

string[] aParams = // array of IDs 
qry = qry.Where(t => aParams.Contains(t.ID)); 

Um jedoch das gegen Entity Framework auszuführen, ich brauche eine Lösung wie die BuildContainsExpression zu schaffen, die auf die Entity Framework gebunden ist. Dies bedeutet, dass ich zwei verschiedene Implementierungen dieses speziellen Filters haben muss, abhängig vom zugrunde liegenden Provider.

Ich würde mich über jeden Hinweis freuen, wie ich von hier fortfahren sollte. Es schien mir, dass die Bereitstellung eines IQueryable aus meinem Repository es mir ermöglichen würde, Filter unabhängig vom zugrundeliegenden Provider durchzuführen, so dass ich bei Bedarf zwischen den Providern wechseln konnte. Das Problem, das ich oben beschrieben habe, lässt mich jedoch denken, dass ich meine gesamte Filterung innerhalb der Repositories durchführen und IEnumerable, IList oder einzelne Klassen zurückgeben sollte.

Vielen Dank, Matt

+0

Falsche Prämisse. 'BuildContainsExpression' ist * völlig * unabhängig von der EF (hängt nur von Linq und Expressions ab). Es ist auch völlig unnötig in EF 4, das 'IEnumerable.Contains' direkt unterstützt. –

Antwort

14

Dies ist eine sehr beliebte Frage. Eine, die ich mich ständig frage. Ich habe es immer am besten empfunden, IEnumerable statt IQueryable aus einem Repository zurückzugeben.

Der Zweck eines Repositorys besteht darin, die Datenbankinfrastruktur zu kapseln, damit sich der Client nicht um die Datenquelle kümmern muss. Wenn Sie jedoch IQueryable zurückgeben, sind Sie dem Verbraucher ausgeliefert, welche Art von Abfrage für Ihre Datenbank ausgeführt wird, und ob sie etwas tun werden, das der LINQ-Anbieter nicht unterstützt.

Nehmen Sie z. B. Paging. Nehmen wir an, Sie haben eine Customer Entity und Ihre Datenbank könnte Hunderttausende von Kunden haben. Welchen Code würden Sie lieber Ihren Kunden schreiben lassen?

var customers = repos.GetCustomers().Skip(skipCount).Take(pageSize).ToList(); 

ODER

var customers = repos.GetCustomers(pageIndex, pageSize); 

Im ersten Ansatz machen Sie es unmöglich für das Repository der Anzahl der Datensätze aus der Datenquelle abgerufen zu beschränken. Außerdem muss Ihr Kunde den skipCount berechnen.

Im zweiten Ansatz bieten Sie Ihrem Client eine grobkörnigere Schnittstelle.Jetzt kann Ihr Repository einige Einschränkungen für pageSize erzwingen, um die Abfrage zu optimieren. Sie kapseln auch die Berechnung von skipCount.

Wie auch immer, in Ihrer Situation ist Ihr Kunde Ihr Service. Ich nehme an, die Frage geht wirklich auf eine Trennung der Sorgen hinaus. Wo ist es besser, eine solche Validierungslogik durchzuführen? Nun, diese Antwort kann sehr gut "im Dienst" sein. Aber was ist mit der Antwort auf "Wo ist es besser, Abfragelogik zu enthalten?". Für mich ist die Antwort eindeutig "The Repository". Das ist das beabsichtigte Fachgebiet.