Abhängigkeitsinjektion mit n-Tier-Entity-Framework-Lösung

12

Ich entwerfe derzeit eine n-Tier-Lösung, die Entity Framework 5 (.net 4) als Datenzugriffsstrategie verwendet, bin jedoch besorgt darüber, wie die Abhängigkeitsinjektion integriert werden kann, um sie testbar / flexibel zu machen.

Mein aktuelles Lösungslayout sieht wie folgt aus (meine Lösung heißt Alcatraz):

Alcatraz.WebUI : Ein asp.net-Webform-Projekt, die Front-End-Benutzeroberfläche, verweist auf die Projekte Alcatraz.Business und Alcatraz.Data.Models .

Alcatraz.Business : Ein Klassenbibliotheksprojekt, enthält die Geschäftslogik, verweist auf Projekte Alcatraz.Data.Access , Alcatraz.Data.Models

Alcatraz.Data.Access : Ein Klassenbibliotheksprojekt, das AlcatrazModel.edmx und AlcatrazEntitiesDbContext enthält, verweist auf Projekte Alcatraz.Data.Models .

Alcatraz.Data.Models : Ein Klassenbibliotheksprojekt, enthält POCOs für das Alcatraz-Modell, keine Referenzen.

Meine Vision für die Funktionsweise dieser Lösung ist, dass die Web-Benutzeroberfläche ein Repository innerhalb der Geschäftsbibliothek instanziiert und dieses Repository eine Abhängigkeit (über den Konstruktor) von einer Verbindungszeichenfolge (keine AlcatrazEntitiesInstanz) aufweist. Die Web-Benutzeroberfläche würde die Datenbankverbindungszeichenfolgen kennen, aber nicht, dass es sich um eine Entity Framework-Verbindungszeichenfolge handelt.

Im Business-Projekt:

public class InmateRepository : IInmateRepository
{
    private string _connectionString;

    public InmateRepository(string connectionString)
    {
        if (connectionString == null)
        {
            throw new ArgumentNullException("connectionString");
        }

        EntityConnectionStringBuilder connectionBuilder = new EntityConnectionStringBuilder();

        connectionBuilder.Metadata = "res://*/AlcatrazModel.csdl|res://*/AlcatrazModel.ssdl|res://*/AlcatrazModel.msl";
        connectionBuilder.Provider = "System.Data.SqlClient";
        connectionBuilder.ProviderConnectionString = connectionString;

        _connectionString = connectionBuilder.ToString();
    }

    public IQueryable<Inmate> GetAllInmates()
    {
        AlcatrazEntities ents = new AlcatrazEntities(_connectionString);

        return ents.Inmates;
    }
}

In der Web-Benutzeroberfläche:

IInmateRepository inmateRepo = new InmateRepository(@"data source=MATTHEW-PC\SQLEXPRESS;initial catalog=Alcatraz;integrated security=True;");

List<Inmate> deathRowInmates = inmateRepo.GetAllInmates().Where(i => i.OnDeathRow).ToList();

Ich habe ein paar verwandte Fragen zu diesem Design.

  1. Ist dieses Design in Bezug auf die Entity Frameworks-Funktionen überhaupt sinnvoll? Ich habe gehört, dass das Entity-Framework bereits das Unit-of-Work-Muster verwendet. Füge ich nur unnötigerweise eine weitere abstrakte Ebene hinzu?

  2. Ich möchte nicht, dass meine Web-Benutzeroberfläche direkt mit Entity Framework kommuniziert (oder auch nur darauf Bezug nimmt). Ich möchte, dass der gesamte Datenbankzugriff über die Business-Schicht erfolgt, da in Zukunft mehrere Projekte auf derselben Business-Schicht ausgeführt werden (Webservice, Windows-Anwendung usw.) und ich möchten, dass die Geschäftslogik in einem zentralen Bereich verwaltet / aktualisiert werden kann. Ist dies ein geeigneter Weg, um dies zu erreichen?

  3. Sollte die Business-Schicht überhaupt Repositorys enthalten, oder sollte diese in der Access-Schicht enthalten sein? Wenn es in Ordnung ist, wo sie sich befinden, ist die Übergabe einer Verbindungszeichenfolge eine gute Abhängigkeit, die anzunehmen ist?

Vielen Dank, dass Sie sich die Zeit zum Lesen genommen haben!

Matthew
quelle

Antworten:

11

Die Art und Weise, wie Sie DI tun, ist falsch.

Erstens gehört die Verbindungszeichenfolge in die Datenschicht. Oder in der Datei web.config.

Die nächste Abstraktion, mit der Sie sich befassen werden, ist der DbContext, keine Verbindungszeichenfolge. Ihre Repositorys sollten keine Informationen zu Verbindungszeichenfolgen haben. Ihre Geschäftslogik kennt DbContext usw. nicht.

Ihre Benutzeroberfläche hat keine Ahnung und instanziiert nichts, was mit EF zu tun hat.

Konkrete Antworten auf Ihre Punkte:

  1. Fügen Sie keine Abstraktionen hinzu, bis Sie mit EF vertraut sind. Es fügt bereits gute Abstraktionen wie UOW, Abfragen, Verwendung von POCOs usw. hinzu.

  2. Damit DI funktioniert, haben Sie ein Kompositionsstammverzeichnis, das auf alle benötigten Komponenten verweist. Dies kann im WebUI-Projekt der Fall sein oder nicht. Wenn dies nicht der Fall ist, sollten Sie damit rechnen, dass kein Verweis auf EF oder eine andere datenbezogene Technologie vorhanden ist.

  3. Halt hier an. Hören Sie auf, Abstraktionen über Abstraktionen hinzuzufügen. Beginnen Sie mit direkter und "naiver" Architektur und entwickeln Sie diese im Laufe der Zeit weiter.

Abstraktionen sind ein Werkzeug, um mit Komplexität umzugehen. Fehlende Komplexität bedeutet, dass (noch) keine Abstraktionen erforderlich sind.

Boris Yankov
quelle
Um klar zu sein, dass ich verstehe, was Sie sagen: Das Repository (das die Schnittstelle im Geschäft und das konkrete in Alcatraz.Data.Access enthält?) Akzeptiert a DbContextals seine Abhängigkeit. Die Geschäftsklassen haben Repositorys als Abhängigkeit. Für die Abhängigkeitsinjektion mache ich dies manuell (damit ich verstehe, was los ist). Der Grund, warum ich die Verbindungszeichenfolge in festlegen möchte, DbContextist, dass ich Datenbank-Sharding verwende. In bestimmten Fällen muss ich über ein Entity-Framework verfügen, um eine Verbindung zu verschiedenen Datenbanken (mit derselben Struktur) herzustellen . Verstehe ich dich richtig
Matthew
Aus dem von Ihnen bereitgestellten Code geht hervor, dass Sie DI überhaupt nicht ausführen. Das Hauptziel von DI ist es, Sie und Ihren Code von der Verwaltung der Abhängigkeiten zu befreien. Ich kann mir nicht vorstellen, dass Sie es effektiv manuell ohne DI-Container machen.
Boris Yankov
halte auch deinen Geist offen mit DI. Ich habe hier schon zum Spaß genau die gleiche Frage gestellt wie in einem anderen Forum, um gegensätzliche Antworten zu erhalten. DI ist ein Muster, keine Architektur. Abhängig von Ihrem Ziel können Sie entscheiden, ob Sie es verwenden möchten oder nicht. Ich benutze es, aber nicht aus den Gründen, die mir die meisten Leute sagen, dass ich es benutzen soll.
Bastien Vandamme
4

Ein paar kurze Kommentare. Ich persönlich würde wahrscheinlich keine Verbindungszeichenfolge übergeben. Wenn überhaupt würde ich versuchen, Schnittstellen möglicherweise für die Behälter zu verursachen und die Schnittstellen gerade herum zu führen? Lassen Sie die Repositorys eine IOW-Schnittstelle implementieren oder verfügbar machen.

Auf diese Weise muss es sich nicht unbedingt um eine Datenbank handeln, die Ihre Repositorys implementiert. Es könnte sich um einen Cache im Speicher handeln oder um etwas anderes. Vielleicht könnten Sie dann eine Art Abhängigkeitsinjektions-Framework verwenden, um diese sogar zu instanziieren?

Also als Antwort auf einige Ihrer Fragen:

  1. Ja ich finde es ok
  2. Ich hätte immer noch die UI-Referenz das EF-Projekt und die Business-Layer-Referenzschnittstellen, die der EF-Repository-Layer implementiert. Auf diese Weise könnten andere Projekte zwar dieselben Baugruppen verwenden, sie haben jedoch die Flexibilität, diese bei Bedarf auszutauschen.
  3. hmmm, wahrscheinlich die Repositories in der Access-Schicht, aber die Implementierung einer Schnittstellendefinition in der Business-Schicht?

Dies sind nur ein paar Gedanken zum Nachdenken.

dreza
quelle
In Bezug auf Punkt 2 bestand ein Ziel darin, CRUD nicht direkt in der UI-Ebene zu haben. Was ich damit meine, ist, dass ich sicherstellen möchte, dass nur CRUD möglich ist, indem ich die Business-Schicht durchgehe, so wie es verwaltet wird.
Matthew