wake-up-neo.net

Entity Framework: Eine Datenbank, mehrere DbContexts. Ist das eine schlechte Idee?

Mein bisheriger Eindruck war, dass ein DbContext Ihre Datenbank darstellen soll. Wenn Ihre Anwendung eine Datenbank verwendet, möchten Sie also nur einen DbContext. Einige Kollegen möchten jedoch Funktionsbereiche in separate DbContext-Klassen unterteilen. Ich glaube, das kommt von einem guten Ort - dem Wunsch, den Code sauberer zu halten -, aber es scheint unbeständig zu sein. Mein Bauch sagt mir, dass dies eine schlechte Idee ist, aber leider ist mein Bauchgefühl keine ausreichende Voraussetzung für eine Designentscheidung.

Ich suche also nach A) konkreten Beispielen, warum dies eine schlechte Idee sein könnte, oder B) die Zusicherung, dass dies alles gut funktioniert.

171
Josh Schultz

Sie können mehrere Kontexte für eine einzelne Datenbank haben. Dies kann beispielsweise nützlich sein, wenn Ihre Datenbank mehrere Datenbankschemas enthält und Sie jedes als separaten, eigenständigen Bereich behandeln möchten. 

Das Problem ist, wenn Sie zum Erstellen Ihrer Datenbank zuerst Code verwenden möchten - dies kann nur ein einzelner Kontext in Ihrer Anwendung. Der Trick dafür ist normalerweise ein zusätzlicher Kontext, der alle Ihre Entitäten enthält und nur für die Datenbankerstellung verwendet wird. Für Ihre realen Anwendungskontexte, die nur Teilmengen Ihrer Entitäten enthalten, muss der Datenbankinitialisierer auf null gesetzt sein.

Bei der Verwendung mehrerer Kontexttypen treten andere Probleme auf, z. B. gemeinsam genutzte Entitätstypen und deren Weitergabe von einem Kontext an einen anderen usw. Im Allgemeinen ist es möglich, dass Ihr Entwurf wesentlich sauberer wird und verschiedene Funktionsbereiche voneinander getrennt werden Kosten in zusätzlicher Komplexität.

143
Ladislav Mrnka

Dieser Thread sprudelte gerade auf StackOverflow und deshalb wollte ich noch eine "B) Zusicherung geben, dass dies alles in Ordnung sein wird" :)

Genau das mache ich mit dem DDD Bounded Context-Muster. Ich habe darüber in meinem Buch "Programming Entity Framework: DbContext" geschrieben, und es ist der Fokus eines 50-minütigen Moduls in einem meiner Kurse zu Pluralsight -> http://pluralsight.com/training/Courses/TableOfContents/ Efarchitektur

48
Julie Lerman

Festlegen von Kontexten durch Festlegen des Standardschemas

In EF6 können Sie mehrere Kontexte haben. Geben Sie einfach den Namen des Standarddatenbankschemas in der OnModelCreating-Methode der von Ihnen abgeleiteten DbContext-Klasse an (in der sich die Fluent-API-Konfiguration befindet) ..__ Dies funktioniert in EF6:

public partial class CustomerModel : DbContext
{   
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema("Customer");

        // Fluent API configuration
    }   
}

In diesem Beispiel wird "Customer" als Präfix für Ihre Datenbanktabellen verwendet (anstelle von "dbo") . Noch wichtiger ist, dass der/den __MigrationHistory-Tabelle (n) ein Präfix vorangestellt wird, z. Customer.__MigrationHistory. Sie können also mehr als eine __MigrationHistory-Tabelle in einer einzigen Datenbank haben, eine für jeden Kontext. Die Änderungen, die Sie für einen Kontext vornehmen, werden sich nicht mit dem anderen ändern.

Geben Sie beim Hinzufügen der Migration den vollständig qualifizierten Namen Ihrer Konfigurationsklasse (abgeleitet von DbMigrationsConfiguration) als Parameter im Befehl add-migration an:

add-migration NAME_OF_MIGRATION -ConfigurationTypeName FULLY_QUALIFIED_NAME_OF_CONFIGURATION_CLASS


Ein kurzes Wort zum Kontextschlüssel

Gemäß diesem MSDN-Artikel " Kapitel - Mehrere Modelle, die auf dieselbe Datenbank abzielen " "würde EF 6 die Situation wahrscheinlich selbst dann handhaben, wenn nur eine MigrationHistory-Tabelle vorhanden wäre, da in der Tabelle eine ContextKey -Spalte zur Unterscheidung der Migrationen.

Ich ziehe es jedoch vor, mehr als eine MigrationHistory-Tabelle zu haben, indem ich das Standardschema wie oben beschrieben spezifiziere.


Verwenden separater Migrationsordner

In einem solchen Szenario möchten Sie möglicherweise auch mit anderen "Migration" -Ordnern in Ihrem Projekt arbeiten. Sie können Ihre abgeleitete DbMigrationsConfiguration-Klasse mit der MigrationsDirectory-Eigenschaft entsprechend einrichten:

internal sealed class ConfigurationA : DbMigrationsConfiguration<ModelA>
{
    public ConfigurationA()
    {
        AutomaticMigrationsEnabled = false;
        MigrationsDirectory = @"Migrations\ModelA";
    }
}

internal sealed class ConfigurationB : DbMigrationsConfiguration<ModelB>
{
    public ConfigurationB()
    {
        AutomaticMigrationsEnabled = false;
        MigrationsDirectory = @"Migrations\ModelB";
    }
}


Zusammenfassung

Alles in allem kann man sagen, dass alles sauber getrennt ist: Kontexte, Migrationsordner im Projekt und Tabellen in der Datenbank.

Ich würde eine solche Lösung wählen, wenn es Gruppen von Entitäten gibt, die Teil eines größeren Themas sind, jedoch nicht über Fremdschlüssel miteinander verbunden sind.

Wenn die Entitätsgruppen nichts zu tun haben, würde ich für jede eine eigene Datenbank erstellen und auch in verschiedenen Projekten darauf zugreifen, wahrscheinlich mit einem einzigen Kontext in jedem Projekt.

40
Martin

Ich lehne mich gegen die Idee ab, mit realer Erfahrung, um meine Stimme zu stützen. 

Ich wurde an eine große Anwendung herangeführt, die fünf Kontexte für eine einzige Datenbank hatte. Am Ende haben wir am Ende alle Kontexte mit Ausnahme eines entfernt - wir kehren zu einem einzigen Kontext zurück.

Zunächst scheint die Idee mehrerer Kontexte eine gute Idee zu sein. Wir können unseren Datenzugriff in Domänen aufteilen und mehrere einfache, schlanke Kontexte bereitstellen. Klingt nach DDD, richtig? Dies würde unseren Datenzugriff vereinfachen. Ein anderes Argument ist für die Leistung, da wir nur auf den Kontext zugreifen, den wir benötigen.

In der Praxis haben jedoch, da unsere Anwendung wuchs, viele unserer Tabellen Beziehungen in unseren verschiedenen Kontexten. Bei Abfragen an Tabelle A in Kontext 1 musste beispielsweise auch Tabelle B in Kontext 2 verbunden werden. 

Dies hat uns mit ein paar schlechten Entscheidungen gelassen. Wir könnten die Tabellen in den verschiedenen Kontexten duplizieren. Wir haben das versucht. Dies führte zu mehreren Zuordnungsproblemen, einschließlich einer EF-Einschränkung, bei der jede Entität einen eindeutigen Namen haben muss. So endeten wir in den verschiedenen Kontexten mit Entitäten namens Person1 und Person2. Man könnte behaupten, dies sei ein schlechtes Design von unserer Seite, aber trotz unserer besten Anstrengungen wuchs unsere Anwendung tatsächlich in der realen Welt. 

Wir haben auch versucht, beide Kontexte abzufragen, um die benötigten Daten zu erhalten. Zum Beispiel würde unsere Geschäftslogik die Hälfte dessen, was sie brauchte, aus Kontext 1 und die andere Hälfte aus Kontext 2 abfragen. Dies hatte einige wichtige Probleme. Anstatt eine Abfrage für einen einzelnen Kontext auszuführen, mussten wir mehrere Abfragen in verschiedenen Kontexten durchführen. Dies hat eine echte Leistungsstrafe zur Folge. 

Am Ende ist die gute Nachricht, dass es einfach war, die verschiedenen Kontexte herauszulösen. Der Kontext soll ein leichtes Objekt sein. Ich denke nicht, dass Leistung ein gutes Argument für mehrere Kontexte ist. In fast allen Fällen glaube ich, dass ein einziger Kontext einfacher und weniger komplex ist und wahrscheinlich bessere Ergebnisse erzielen wird, und Sie müssen nicht eine Reihe von Workarounds implementieren, damit er funktioniert. 

Ich dachte an eine Situation, in der mehrere Kontexte nützlich sein könnten. Ein separater Kontext könnte verwendet werden, um ein physisches Problem mit der Datenbank zu beheben, in der sich tatsächlich mehr als eine Domäne befindet. Idealerweise wäre ein Kontext Eins-zu-Eins zu einer Domäne, der Eins-zu-Eins zu einer Datenbank. Mit anderen Worten, wenn eine Gruppe von Tabellen in keiner Weise mit den anderen Tabellen in einer bestimmten Datenbank zusammenhängt, sollten sie wahrscheinlich in eine separate Datenbank abgerufen werden. Mir ist klar, dass dies nicht immer praktisch ist. Wenn jedoch ein Satz von Tabellen so unterschiedlich ist, dass Sie sich wohl fühlen würden, sie in eine separate Datenbank zu unterteilen (aber Sie entscheiden sich nicht dafür), könnte ich den Fall für die Verwendung eines separaten Kontexts betrachten, aber nur, weil es tatsächlich zwei separate Domänen gibt.

Ich interessiere mich für deine Gedanken. 

38

Zur Erinnerung: Wenn Sie mehrere Kontexte kombinieren, stellen Sie sicher, dass Sie ausschneiden und alle Funktionen Ihrer verschiedenen RealContexts.OnModelCreating() in Ihre einzelnen CombinedContext.OnModelCreating() einfügen.

Ich habe nur Zeit damit verbracht, nachzusehen, warum meine Kaskaden-Löschbeziehungen nicht beibehalten wurden, nur um festzustellen, dass ich den modelBuilder.Entity<T>()....WillCascadeOnDelete();-Code nicht aus meinem realen Kontext in meinen kombinierten Kontext portiert hatte.

6
Ilan

Ein einfaches Beispiel, um das Folgende zu erreichen:

    ApplicationDbContext forumDB = new ApplicationDbContext();
    MonitorDbContext monitor = new MonitorDbContext();

Umfassen Sie die Eigenschaften einfach im Hauptkontext: (wird zum Erstellen und Verwalten der Datenbank verwendet) Hinweis: Verwenden Sie einfach protected: (Entity ist hier nicht verfügbar).

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("QAForum", throwIfV1Schema: false)
    {

    }
    protected DbSet<Diagnostic> Diagnostics { get; set; }
    public DbSet<Forum> Forums { get; set; }
    public DbSet<Post> Posts { get; set; }
    public DbSet<Thread> Threads { get; set; }
    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }
}

MonitorContext: Hier separate Entität anzeigen

public class MonitorDbContext: DbContext
{
    public MonitorDbContext()
        : base("QAForum")
    {

    }
    public DbSet<Diagnostic> Diagnostics { get; set; }
    // add more here
}

Diagnosemodell:

public class Diagnostic
{
    [Key]
    public Guid DiagnosticID { get; set; }
    public string ApplicationName { get; set; }
    public DateTime DiagnosticTime { get; set; }
    public string Data { get; set; }
}

Wenn Sie möchten, können Sie alle Entitäten innerhalb des Haupt-ApplicationDbContext-Objekts als geschützt markieren. Erstellen Sie dann nach Bedarf zusätzliche Kontexte für jede Trennung von Schemata.

Sie verwenden alle dieselbe Verbindungszeichenfolge, verwenden jedoch separate Verbindungen. Übergreifen Sie also keine Transaktionen und machen Sie sich der Sperrprobleme bewusst. Im Allgemeinen sollte Ihre Trennung so gestaltet werden, dass dies sowieso nicht passieren sollte.

6
Choco

Mein Bauch sagte mir dasselbe, als ich auf dieses Design stieß.

Ich arbeite an einer Codebasis, bei der drei dbContexts zu einer Datenbank gehören. 2 der 3 Datenbankkontexte sind von Informationen aus 1 Datenbankkontext abhängig, da sie die Verwaltungsdaten bereitstellen. Dieser Entwurf hat Einschränkungen für die Abfrage Ihrer Daten auferlegt. Ich bin auf dieses Problem gestoßen, bei dem Sie nicht über dbcontexts mitmachen können. Stattdessen müssen Sie die beiden separaten dbcontexts abfragen und dann eine Verknüpfung im Speicher erstellen oder beide durchlaufen, um die Kombination der beiden als Ergebnismenge zu erhalten. Das Problem dabei ist, anstatt eine bestimmte Ergebnismenge abzufragen, laden Sie jetzt alle Ihre Datensätze in den Arbeitsspeicher und führen dann einen Join mit den beiden Ergebnissätzen im Arbeitsspeicher durch. Es kann wirklich langsamer werden .

Ich würde die Frage "" nur weil Sie können, sollten Sie? "

In diesem Artikel finden Sie das Problem, das mir in Bezug auf dieses Design begegnet ist . Der angegebene LINQ-Ausdruck enthält Verweise auf Abfragen, die verschiedenen Kontexten zugeordnet sind

4

Noch ein bisschen "Weisheit". Ich habe eine Datenbank, auf die sowohl das Internet als auch eine interne App zugreifen. Ich habe für jedes Gesicht einen Kontext. Das hilft mir, eine disziplinierte, sichere Trennung zu wahren.

2
Miguel Delgado

Im Code können Sie zunächst mehrere DBContext-Dateien und nur eine Datenbank haben. Sie müssen nur die Verbindungszeichenfolge im Konstruktor angeben.

public class MovieDBContext : DbContext
{
    public MovieDBContext()
        : base("DefaultConnection")
    {

    }
    public DbSet<Movie> Movies { get; set; }
}
2
Daniel

Inspiriert von [@JulieLermans DDD MSDN Mag-Artikel 2013] [1]

    public class ShippingContext : BaseContext<ShippingContext>
{
  public DbSet<Shipment> Shipments { get; set; }
  public DbSet<Shipper> Shippers { get; set; }
  public DbSet<OrderShippingDetail> Order { get; set; } //Orders table
  public DbSet<ItemToBeShipped> ItemsToBeShipped { get; set; }
  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    modelBuilder.Ignore<LineItem>();
    modelBuilder.Ignore<Order>();
    modelBuilder.Configurations.Add(new ShippingAddressMap());
  }
}

public class BaseContext<TContext>
  DbContext where TContext : DbContext
{
  static BaseContext()
  {
    Database.SetInitializer<TContext>(null);
  }
  protected BaseContext() : base("DPSalesDatabase")
  {}
}   

"Wenn Sie eine Neuentwicklung durchführen und Code First Ihre Datenbank basierend auf Ihren Klassen erstellen oder migrieren möchten, müssen Sie ein" Uber-Modell "mit einem DbContext erstellen, der alle erforderlichen Klassen und Beziehungen enthält Erstellen Sie ein vollständiges Modell, das die Datenbank darstellt. Dieser Kontext darf jedoch nicht von BaseContext erben. " J L

1
OzBob

Ich möchte einen Fall teilen, in dem es meiner Meinung nach sinnvoll ist, mehrere DBContexts in derselben Datenbank zu haben. 

Ich habe eine Lösung mit zwei Datenbanken. Eine ist für Domain-Daten mit Ausnahme von Benutzerinformationen. Die andere dient ausschließlich zur Information der Benutzer. Diese Einteilung ist in erster Linie von der EU Datenschutz-Grundverordnung bestimmt. Durch zwei Datenbanken kann ich die Domänendaten frei verschieben (z. B. von Azure in meine Entwicklungsumgebung), solange die Benutzerdaten an einem sicheren Ort bleiben. 

Nun habe ich für die Benutzerdatenbank zwei Schemata über EF implementiert. Einer ist der Standard, der vom AspNet Identity Framework bereitgestellt wird. Die andere ist unsere eigene Implementierung aller anderen benutzerbezogenen. Ich bevorzuge diese Lösung der Erweiterung des ApsNet-Schemas, da ich zukünftige Änderungen an AspNet Identity problemlos handhaben kann und gleichzeitig die Trennung den Programmierern deutlich macht, dass "unsere eigenen Benutzerinformationen" in das von uns definierte spezifische Benutzerschema gehen .

0
freilebt