MVC, ORM und Datenzugriffsmuster

8

Ich glaube, ich habe diesen Zustand "Lähmung durch Analyse" getroffen. Ich habe eine MVC-App, die EF als ORM verwendet. Also versuche ich mich für das beste Datenzugriffsmuster zu entscheiden, und bisher denke ich, dass es die beste Lösung ist, alle Datenzugriffslogik in Controller zu integrieren. Aber es klingt irgendwie nicht richtig. Eine weitere Option ist das Erstellen eines externen Repository, das Dateninteraktionen verarbeitet. Hier sind meine Vor- / Nachteile:

Wenn ich den Datenzugriff auf die Controller einbette, bekomme ich einen Code wie diesen:

%Vor%

Damit bekomme ich volle Vorteile von Lazy Loading, ich schließe die Verbindung direkt nachdem ich fertig bin, ich bin flexibel auf Where-Klausel - all die Nettigkeiten. Die Con - Ich vermische eine Menge Sachen in einer einzigen Methode - Datenprüfung, Datenzugriff, UI-Interaktionen.

Mit dem Repository exportiere ich den Datenzugriff und kann theoretisch Repos ersetzen, wenn ich mich entscheide, ado.net zu verwenden oder mit einer anderen Datenbank zu arbeiten. Aber ich sehe keinen guten, sauberen Weg, lazy loading zu realisieren, und wie man DbContext / Connection Lifetime kontrolliert. Angenommen, ich habe eine IRepository-Schnittstelle mit CRUD-Methoden. Wie würde ich eine Liste von Adressen laden, die zu einem bestimmten Benutzer gehören? Methoden wie GetAddressListByUserId aussehen hässlich, falsch, und mich dazu bringen, eine Reihe von Methoden zu erstellen, die genauso hässlich sind und wenig Sinn machen, wenn man ORM benutzt.

Ich bin mir sicher, dass dieses Problem millionenfach gelöst wurde und hoffe, dass es irgendwo eine Lösung gibt.

Und noch eine Frage zum Repository-Muster - wie gehst du mit Objekten um, die Eigenschaften sind? Z.B. Der Benutzer hat eine Liste von Adressen. Wie würden Sie diese Liste abrufen? Erstellen Sie ein Repository für die Adresse? Bei ORM muss das Adressobjekt weder einen Verweis zurück auf den Benutzer noch ein Id-Feld haben, mit Repo muss es alles haben. Mehr Code, mehr exponierte Eigenschaften ..

    
Evgeni 17.03.2011, 13:18
quelle

3 Antworten

7

Der gewählte Ansatz hängt stark von der Art des Projekts ab, mit dem Sie arbeiten werden. Für kleine Projekte, bei denen ein Rapid Application Development ( RAD ) Ansatz erforderlich ist, könnte es fast in Ordnung sein, Ihr EF-Modell direkt im MVC-Projekt zu verwenden und Datenzugriff in den Controllern zu haben. Je mehr das Projekt wächst, desto chaotischer es wird werden und du wirst anfangen in immer mehr Probleme zu laufen. Wenn Sie gutes Design und Wartungsfreundlichkeit wünschen, gibt es verschiedene Ansätze, aber im Allgemeinen können Sie sich an Folgendes halten:

Halten Sie Ihre Controller und Ansichten sauber. Controller sollten nur den Anwendungsfluss steuern und keinen Datenzugriff oder gar Geschäftslogik enthalten. Ansichten sollten nur für die Präsentation verwendet werden - geben Sie ihr ein ViewModel und es wird sie als HTML darstellen (keine Geschäftslogik oder Berechnungen). Eine ViewModel pro Ansicht ist eine ziemlich saubere Art, dies zu tun.

Eine typische Controller-Aktion würde folgendermaßen aussehen:

%Vor%

Aus den oben genannten Gründen ist es gut, Ihren Datenzugriff zu abstrahieren (Testbarkeit, einfacher Wechsel des Datenanbieters oder ORM oder was auch immer, etc.). Das Muster Repository ist eine gute Wahl, aber hier erhalten Sie auch ein paar Implementierungsoptionen. Es gab immer eine Menge Diskussionen über generische / nicht-generische Repositories, ob man IQueryable s zurückgeben sollte, oder nicht. Aber letztendlich ist es für dich das Richtige.

Übrigens, warum willst du lazy loading? In der Regel wissen Sie genau, welche Daten Sie für eine bestimmte Ansicht benötigen. Warum also sollten Sie sie verzögerungsfrei abrufen, um zusätzliche Datenbankaufrufe zu tätigen, anstatt alles in einem einzigen Aufruf zu laden? Persönlich denke ich, dass es in Ordnung ist, mehrere Get -Methoden zum Abrufen von Objekten mit oder ohne Kinder zu haben. Z.B.

%Vor%

Es mag etwas übertrieben erscheinen und Sie können einen anderen Ansatz wählen, aber solange Sie ein Muster haben, dem Sie folgen, ist die Pflege des Codes viel einfacher.

    
Yakimych 17.03.2011, 13:57
quelle
5

Persönlich mache ich es so:

Ich habe eine abstrakte Domain-Ebene, die Methoden hat, nicht nur CRUD, sondern spezialisierte Methoden, zum Beispiel UsersManager.Authenticate (), usw. Sie verwendet Datenzugriffslogik oder Datenzugriffsschicht-Abstraktion (abhängig von der Ebene der Abstraktion muss ich haben).

Es ist immer besser, zumindest eine abstrakte Abhängigkeit zu haben. Hier sind einige Vorteile davon:

  • Sie können zu einem späteren Zeitpunkt eine Implementierung durch eine andere ersetzen.
  • Sie können Ihren Controller bei Bedarf testen.

Geben Sie dem Controller selbst zwei Konstruktoren an: einen mit einer abstrakten Domänenzugriffsklasse (z. B. Fassade der Domäne) und einen anderen (leeren) Konstruktor, der die Standardimplementierung auswählt. Auf diese Weise lebt Ihr Controller während der Laufzeit der Webanwendung (Aufruf des leeren Konstruktors) und während des Komponententests (mit der injizierten Scheindomänenschicht) gut.

Um zu einem späteren Zeitpunkt problemlos zu einer anderen Domain wechseln zu können, müssen Sie den Domain-Creator anstelle der Domain selbst injizieren. Auf diese Weise können Sie, indem Sie die Domänenlayerkonstruktion für den Domänenersteller lokalisieren, jederzeit zu einer anderen Implementierung wechseln, indem Sie einfach den Domänenersteller rekonstruieren (durch den Hersteller meine ich eine Art Fabrik).

Ich hoffe, das hilft.

Zusatz :

  • Ich würde CRUD-Methoden in der Domänenebene nicht empfehlen, da dies zu einem Albtraum wird, wenn Sie die Einheitentestphase ausführen, oder sogar noch mehr, wenn Sie die Implementierung zu einem späteren Zeitpunkt in die neue Version ändern müssen / li>
Tengiz 17.03.2011 13:33
quelle
0

Es kommt wirklich darauf an, wo Sie Ihren Code haben möchten. Wenn Sie einen Datenzugriff für ein Objekt benötigen, können Sie es hinter ein IRepository-Objekt oder in den Controller einfügen: Sie werden immer noch mit einer Reihe von GetByXXX-Aufrufen oder dem entsprechenden Code enden. In beiden Fällen können Sie die Lebensdauer der Verbindung verzögern und steuern. Jetzt müssen Sie sich fragen: Wo soll mein Code leben?

Persönlich würde ich argumentieren, es aus dem Controller zu bekommen. Damit meine ich, es auf eine andere Ebene zu verschieben. Wahrscheinlich verwenden Sie einen IRespository-Mustertyp, in dem Sie eine Reihe von GetByXXX-Aufrufen haben. Sicher, sie sind hässlich. Falsch? Ich würde anders argumentieren. Zumindest sind sie alle zusammen in derselben logischen Schicht enthalten, anstatt über die Controller verstreut zu sein, wo sie mit Validierungscode usw. vermischt sind.

    
Ken Brittain 17.03.2011 13:40
quelle