Generisches Repository, Data Access Layer
Ich bin erstellen Sie ein neues Projekt mithilfe von business objects (Mitarbeiter, Produkt). Aufgrund von Einschränkungen, ich bin nicht mit LINQ to SQL oder jede ORM-Mapper.
Habe ich hand-code der Data Access Layer(s). Ich bin daran interessiert, mit dem Repository-Pattern".
Nach dem, was ich verstehe, ich habe ein generic repository IRepository
implementiert durch alle repositories ProductRepository, EmployeeRepository
.
Was mich verwirrt ist, dass verschiedene business-Objekte haben unterschiedliche Anforderungen. Zum Beispiel:
ProductRepository
GetAllProducts ();
GetProductById (int id);
GetProductByMaxPrice (double price);
GetProductByNamePrice (string name, double Price);
Get... (...);
EmployeeRepository
GetEmployeeByAge ();
GetEmployeeByJob (string description);
GetEmployeeBySalary (double salary);
Get... (...); //and so on
Wie kann ich erstellen ein generisches repository, das erfüllt verschiedene Anforderungen des Datenzugriffs von verschiedenen Objekten?
Habe ich gelesen, sehr viel Theorie in Bezug auf Repository-Muster, aber würde wirklich zu schätzen, ein funktionierendes Beispiel.
Außerdem, wenn ich alle Repositorys verwenden ein generisches repository, mit dem Fabrik-Muster wird einfach gut. Zum Beispiel:
interface IRepository
{
....
}
ProductRepository : IRepository
{
....
}
EmployeeRepository : IRepository
{
....
}
Dann können wir die factory-Muster effektiv wie:
IRepository repository;
repository = new ProductRepository ();
repository.Call_Product_Methods ();
repository = new EmployeeRepository ();
repository.Call_Employee_Methods ();
- Die generische repository-Schnittstelle hat in der Regel nur die standards -- einfügen, erhalten durch ID, erhalten Sie alle, löschen, aktualisieren. Wie sonst könnte es sein, generische?
- Es könnte IQueryable<T> Query { get; }
- Wenn Sie Ihre Methoden sind anders, Sie können nicht die gleichen IRepository. Was Sie wollen, ist eine IEmployeeRepository ... und code, die. Implementieren Sie dann eine oder mehrere konkrete EmployeeRepository ist.
- Dies kann eine Hilfe sein auf Ihrem venture : codeproject.com/Articles/488308/...
- granadaCoder, wenn wir implementieren ein separates repository für jedes Objekt, das wir am Ende erstellen die traidional FAT DALs mit vielen Methoden die es gibt. Gibt es nicht eine elegante Art und Weise zu schreiben, eine DAL?
- Wiktor, wie können wir IQueryable<T> Query { get; } effektiv?
- im Allgemeinen sollte man sich nicht definieren
Insert
undUpdate
generischen Operationen, da diese nicht wirklich vertreten business cases. - elegant = mit NHibernate ist ISession direkt wo es nötig ist. Keine Notwendigkeit zur Abstraktion.
- In meinem Fall, wenn das Repository-Muster nicht gut genug ist, gibt es eine andere alternative, um Daten leicht für verschiedene repositories. Es scheint, dass die einzige Lösung ist, um jedes repository implementiert seine eigene Schnittstelle (EmployeeRepository:IEmployeeRepository, ProductRepository:IProductRepository). Wie ich bereits erwähnt habe, kann ich nicht mit LINQ to SQL oder anderen ORM-Mapper. Jede Hilfe ist willkommen.
- Wer erlegt diese Einschränkungen? Verbot von ein ORM ist lächerlich, ehrlich gesagt. Hand-coded data access ist ein Service-desaster. Wenn Sie möchten, um es gut zu machen, also ohne Wände des raw-SQL und langen Strecken von mapping-code, werden Sie bald selbst schreiben Sie Ihre eigenen ORM! Dies wird sich dramatisch erhöhen die Projekt-Durchlaufzeit. Ich glaube, Sie sollten wirklich versuchen, drehen Sie ein paar Waffen irgendwo.
- In einigen Fällen schreiben die data-access-layer ist die beste Lösung. ORM ist nicht die beste Antwort für alles. Aber vereinbart, dass einige willkürliche Verbot, es ist albern.
- Ich dachte das gleiche, aber der Dapper-micro-ORM (nicht wirklich ein ORM, jedoch ebenso sehr ordentlich helper-Methoden) war wie ein Hauch frischer Luft, um mich gegenüber dem Melasse-wie Entity Framework. Die Vorhersagbarkeit (nie komisch "das kann nicht übersetzt werden, um SQL" oder "N", query-problem), die Freiheit, die die Leistung! Es gibt Nachteile natürlich auch, aber insgesamt würde ich zögern, gehen Sie zurück zu EF in zukünftige Projekte.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Dem repository-Muster ist eine große Muster zu verwenden, aber wenn es nicht richtig gemacht wird, anstatt das Leben leichter, es wird ein großer Schmerz!
So, die beste Möglichkeit, dies zu tun (da Sie nicht verwenden möchten, EF oder anderen ORM) ist durch die Schaffung einer generischen Schnittstelle, und dann eine base abstrakten Implementierung. Auf diese Weise brauchen Sie nicht, um code-repository, können Sie einfach instanziieren Sie von der Art!
Und nach diesem, wenn Sie bestimmte spezifische Methode, um einige Ihrer Einheiten, Sie können alle Erben aus dem Repository und Methoden überschreiben oder hinzufügen und die Eigenschaften als benoetigten.
Wenn Sie möchten, verwenden Sie das Repository-Muster, das ich auch empfehlen, dass Sie die IUnitOfWork Muster, und halten Sie es getrennt aus dem repository.
Beide interfaces sollte wie folgt Aussehen:
Sehr einfache IUnitOfWork:
Und Ihnen, den Repository-interface, Verwendung von Generika:
Methoden .Add(), .Löschen (), sollten Sie nicht senden Sie alles an die Datenbank, aber Sie sollten immer senden die änderungen an der IUnitOfWork (, die Sie implementieren können, in der Sie DAL-Klasse), und nur, wenn Sie rufen .Save () - Methode der IUnitOfWork Sie speichern Dinge in der Datenbank.
Implementierte ich meine Repository-Klasse mit EntityFramework, und das macht die Dinge einfacher, aber Sie können es tun, wie Sie wollen.
Den code ein, den Sie verwenden werden somethin wie diese:
Wie gesagt, mit EF und den DbContext, das ist viel einfacher, hier sind ein kleiner Teil meiner Repository-Klasse:
Diese Weise werden Sie nicht brauchen, um zu bauen ein GetEmployeeByAge, Sie würde einfach schreiben:
Oder Sie könnten einfach direkt anrufen (keine Notwendigkeit zu schaffen, die Methode)
Im Allgemeinen, und meiner Meinung nach, ein generisches repository "Basis" - interface, löst nicht wirklich viel. Einige haben erwähnt, dass es könnte, in der Theorie, bieten eine get-Eigenschaft, die eine Ganzzahl und gibt einen Datensatz. Ja, das ist schön und bequem - und je nach Anwendungsfall, vielleicht sogar wünschenswert.
Wo ich persönlich die Grenze ziehen, ist
Insert
,Update
, undDelete
Methoden. In alle, aber die meisten simpel der Fälle, sollten wir identifizieren was wir tun. Ja, das erstellen einer neuenSupplier
könnte lediglich bedeuten aufrufen einesInsert
Betrieb. Aber die meisten nicht-trivialen Fällen, die Sie tun werden andere Sachen tun.Daher bei der Gestaltung von repositories denke ich, dass es am besten zu identifizieren, was Aktionen, die Sie gehen zu wollen, zu tun und mit Methoden, die genauso heißen, dass:
Sind wir nun definieren, was wir tun mit den Daten. Generische Insert -, Update -, und Delete-Methoden nicht ableiten, Nutzung des repository und könnte möglicherweise dazu führen, dass Missbrauch durch Entwickler die nicht verstehen, was andere zusätzliche Dinge, die passieren müssen, wenn wir tatsächlich gehen und tun etwas.
Also, was ist ein gutes Beispiel für eine base-repository? Naja, was ist ein repository, das caching implementiert? Die base-repository könnte irgendeine Art von cache und unser abgeleiteten repositories verwenden könnte, diesen cache auf die Rückkehr veraltete Daten, die, wenn man so wollte.
Sogar die
this[int]
default-Eigenschaft hat, komplizierte Probleme, wenn wir brauchen, um die Antwort was wir sind wieder. Wenn es sich um ein großes Objekt mit vielen Referenzen, sind wir wieder die ganze Sache mit aller seiner Teile, oder sind wir wieder ein sehr bare-bones-POCO -, mit weiteren Abfragen notwendig, um die Lücken zu füllen. Eine generischethis[int]
muss die Frage nicht beantworten, aber:Sind ziemlich gut definiert, meiner Meinung nach. In diesen Tagen der intellisense, eine Programmierung gegen das repository wird wissen, was er/Sie braucht, um berufen zu bekommen, was Sie wollen, zurück. Wie entscheiden Sie, wie viele von diesen Methoden gibt es? Gut, Sie schauen auf das Produkt, das Sie sich tatsächlich entwickeln. Welche Fälle haben Sie, um Daten... wer bekommt die Daten, und warum sind Sie es? Die meisten der Zeit, diese einfachen Fragen zu beantworten.
Einem gemeinsamen problem, jedoch, ist, wenn wir zulassen möchten, dass Benutzer auf "durchsuchen" Daten in einer tabellarischen form. "Gib mir 'x' Anzahl der Datensätze, sortiert nach dem 'x' - Feld, in ein mit Seitenzahlen Mode... oh, und ich kann oder kann nicht enthalten eine Art von Suche, auf einige Spalte". Diese Art code ist etwas, das Sie wirklich nicht wollen, zu haben, zu implementieren und zu jedes deiner repositories. So kann es ein Fall für manche boiler-plate-Abfrage Konstruktion eines hypothetischen
IRepositoryThatSupportsPagination
. Ich bin sicher, Sie können einen besseren Namen ausdenken für, die.Offensichtlich, es kann gut sein, mehr Fälle. Aber ich würde nie werfen Sie die Standard -
CRUD
Operationen in einem base-repository-Schnittstelle/Klasse, weil es nicht bedeuten alles, mit Ausnahme des unwichtigen, trivialen Fälle.Ja, man könnte einfach schreiben Sie eine elegante DAL-layer, basierend auf einer generischen Konstante repository-Schnittstelle.
Wäre es wahrscheinlich eine lächerlich schlechte Leistung, obwohl.
In einer perfekten Welt, wo alle Informationen abgerufen werden konnten, von der DB ohne Kosten, eine einfache und generische repository genug sein würde. Leider ist das nicht der Fall - es ist besser, bestimmte query-Methoden, für jeden query-operation, die wir wissen, dass unsere Datenbank verarbeiten kann, als ein generisches repository, die generische Abfrage-Methoden, so dass alle Arten von verrückt-Abfragen, die aus der business-Schicht.
Bearbeiten
Ich glaube, Sie scheinen falsch zu sein in Bezug auf einen ganz bestimmten Punkt: das vermeiden der Verwendung einer generischen ORM-Mapping-Bibliothek bedeutet, dass Sie nicht tun, ORM. Dies ist nicht unbedingt wahr.
Es sei denn, Sie setzen generische array-ähnliche Objekte, um die Benutzeroberfläche (das würde auch diese Diskussion über das Repository-Muster komplett nutzlos ist), Sie sind Transformation von relationalen Daten in die Domänen-Objekte. Und das ist genau das, was ORM ist über: die Tatsache, dass Sie nicht mit NHibernate, EF noch LINQ to SQL bedeutet nur, Sie haben viel mehr Arbeit. 🙂
So, Nein, mit dem Repository-Muster noch Sinn macht, egal, ob man mit einer automatischen ORM-tool ist oder nicht.
Natürlich gibt es andere Optionen, wie die Active Record. Dies ist eine einfachere Muster, die mischt das domain-Objekt mit Daten zugreifen, die Logik (und die Verwendung eines ORM-tool ist hier ebenfalls optional).