September 2017

Band 32, Nummer 9

Data Points: EF Core 2.0 ist DDD-freundlicher

Von Julie Lerman

Julie LermanWenn Sie diese Kolumne bereits seit einiger Zeit verfolgen, ist Ihnen vielleicht aufgefallen, dass recht viele Artikel das Implementieren von Entity Framework (EF) beim Erstellen von Lösungen zum Thema hatten, die DDD-Muster (Domain-Driven Design, domänengesteuerter Entwurf) und -Empfehlungen verwenden. Auch wenn bei DDD die Domäne und nicht das persistente Speichern von Daten im Mittelpunkt steht, müssen Daten an einem bestimmten Punkt in Ihre Software gelangen und sie wieder verlassen können.

In den letzten Versionen von EF konnten Benutzer die EF-Muster (konventionell oder angepasst) ohne viele Reibungsverluste zum direkten Zuordnen unkomplizierter Domänenklassen zur Datenbank verwenden. Und meine Empfehlung lautete im Allgemeinen wie folgt: Wenn die EF-Zuordnungsschicht dafür zuständig ist, Ihre gut entworfenen Domänenmodelle in die und aus der Datenbank zu transportieren, ohne dass ein zusätzliches Datenmodell erstellt werden muss, reicht dies aus. Wenn Sie aber damit beginnen, Ihre Domänenklassen und die Programmlogik zu optimieren, damit das Zusammenspiel mit Entity Framework besser funktioniert, ist es höchste Zeit, ein Modell für Datenpersistenz zu erstellen und die Zuordnung dann von den Domänenmodellklassen zu den Datenmodellklassen vorzunehmen.

Ich war ein wenig überrascht, vor wie langer Zeit diese Artikel zu DDD und EF-Zuordnungen veröffentlicht wurden. Es ist schon vier Jahre her, dass ich die dreiteilige Artikelserie mit dem Titel „Codierung für Domain-Driven Design: Tipps für Entwickler mit Datenschwerpunkt“ geschrieben habe, die im August, September und Oktober 2013 in MSDN Magazine veröffentlicht wurde. Der folgende Link führt zum ersten Teil, der Links zur gesamten Artikelserie enthält: msdn.com/magazine/dn342868.

Zwei Kolumnen beschäftigten sich insbesondere mit DDD-Mustern und Erläuterungen, wie EF einfach eine Zuordnung zwischen Ihren Domänenklassen und der Datenbank vornehmen kann (oder eben auch nicht). Ab EF6 bestand eines der größten Probleme darin, dass eine „untergeordnete“ Sammlung nicht gekapselt und damit geschützt werden konnte. Die Verwendung der bekannten Muster zum Schützen der Sammlung (dies bedeutete in den meisten Fällen die Bereitstellung eines IEnumerable-Objekts) war nicht mit den Anforderungen von EF konform, und EF erkannte nicht einmal, dass die Navigation ein Teil des Modells sein sollte. Steve Smith und ich haben viel Zeit damit verbracht, über dieses Thema nachzudenken, als wir unseren Pluralsight-Kurs „Domain-Driven Design Fundamentals“ (bit.ly/PS-DDD) vorbereiteten. Irgendwann ist Steve eine nette Problemumgehung eingefallen (bit.ly/2ufw89D).

EF Core hat dieses Problem schließlich mit Version 1.1 gelöst, und ich habe mich mit diesem Feature in der Kolumne im Januar 2017 befasst (msdn.com/magazine/mt745093). EF Core 1.0 und 1.1 haben auch einige weitere DDD-Einschränkungen beseitigt, aber es gab dennoch Lücken: vorrangig die Unmöglichkeit, in Ihren Domänentypen verwendete DDD-Wertobjekte zuzuordnen. Diese Möglichkeit bestand in EF von Beginn an, wurde aber noch nicht auf EF Core übertragen. Mit der Veröffentlichung von EF Core 2.0 besteht diese Einschränkung nun nicht mehr.

Ich möchte in diesem Artikel die verfügbaren EF Core 2.0-Features vorstellen, die mit vielen der DDD-Konzepte kompatibel sind. EF Core 2.0 ist wesentlich freundlicher zu Entwicklern, die diese Konzepte nutzen, und vielleicht setzen Sie sich in diesem Zusammenhang erstmals mit ihnen auseinander. Auch wenn Sie nicht planen, DDD im großen Stil einzusetzen, können Sie dennoch von den zahlreichen sinnvollen Mustern profitieren! In Kombination mit EF Core gilt das Gesagte umso mehr.

1:1 wird intelligenter

In seinem Buch „Domain-Driven Design“ sagt Eric Evans, dass „eine bidirektionale Zuordnung bedeutet, dass beide Objekte nur zusammen verstanden werden können. Wenn die Anwendungsanforderungen keinen Durchlauf in beide Richtungen erfordern, verringert das Hinzufügen einer Durchlaufrichtung die gegenseitige Abhängigkeit und vereinfacht den Entwurf“. Das Befolgen dieser Empfehlung hat tatsächlich Nebenwirkungen in meinem Code entfernt. EF war immer in der Lage, unidirektionale 1:n- und 1:1-Beziehungen zu verarbeiten. Während ich diesen Artikel schreibe, habe ich sogar erfahren, dass ich die ganze Zeit falsch damit lag, dass eine 1:1-Beziehung, bei der beide Enden erforderlich sind, eine bidirektionale Beziehung erzwingt. Es ist jedoch richtig, dass diese erforderlichen Beziehungen konfiguriert werden mussten. Dies gilt in EF Core nun nicht mehr (mit Ausnahme einiger With-Grenzfälle).

Eine nachteilige Anforderung in EF6 für 1:1-Beziehungen bestand darin, dass die Schlüsseleigenschaft im abhängigen Typ auch als Fremdschlüssel für die Prinzipalentität fungieren musste. Dadurch waren Sie gezwungen, Klassen auf eine seltsame Weise zu entwerfen (auch wenn man sich daran gewöhnen konnte). Dank der Einführung von Unterstützung für eindeutige Fremdschlüssel in EF Core kann nun eine explizite Fremdschlüsseleigenschaft im abhängigen Ende der 1:1-Beziehung verwendet werden. Die Verwendung eines expliziten Fremdschlüssels ist natürlicher. Und in den meisten Fällen sollte EF Core in der Lage sein, das abhängige Ende der Beziehung basierend auf dem Vorhandensein dieser Fremdschlüsseleigenschaft richtig abzuleiten. Wenn dies aufgrund eines Grenzfalls nicht funktioniert, muss eine Konfiguration hinzugefügt werden. Ich zeige dies gleich, wenn ich die Fremdschlüsseleigenschaft umbenenne.

Als Beispiel für eine 1:1-Beziehung verwende ich meine EF Core-Lieblingsdomäne: Klassen aus dem Film „Die sieben Samurai“:

public class Samurai {
  public int Id { get; set; }
  public string Name { get; set; }
  public Entrance Entrance { get; set; }
}

public class Entrance {
  public int Id { get; set; }
  public string SceneName { get; set; }
  public int SamuraiId { get; set; }
}

Mit EF Core wird dieses Paar aus Klassen („Samurai“ und „Entrance“, das erste Auftreten der Figur im Film) nun richtig als eine unidirektionale 1:1-Beziehung identifiziert. Dabei ist „Entrance“ der abhängige Typ. Ich benötige keine Navigationseigenschaft in „Entrance“ und keine besondere Zuordnung in der Fluent-API. Der Fremdschlüssel („SamuraiId“) befolgt die Konvention, daher ist EF Core in der Lage, die Beziehung zu erkennen.

EF Core leitet dies in der Datenbank ab. „Entrance.SamuraiId“ ist ein eindeutiger Fremdschlüssel, der zurück auf „Samurai“ verweist. Beachten Sie einen Punkt, der mir Probleme bereitet hat, weil (daran muss ich mich selbst ständig erinnern) EF Core nicht EF6 ist! Standardmäßig behandeln .NET und EF Core „Samurai.Entrance“ zur Laufzeit als eine optionale Eigenschaft, wenn keine Domänenprogrammlogik vorhanden ist, die erzwingt, dass „Entrance“ eine erforderliche Eigenschaft ist. Ab EF4.3 konnten Sie die Überprüfungs-API nutzen, die auf eine Anmerkung [Required] in der Klasse oder Zuordnung reagierte. In EF Core ist aber (noch?) keine Überprüfungs-API verfügbar, die auf dieses Problem hinweisen könnte. Und auch weitere Anforderungen beziehen sich auf die Datenbank. „Entrance.SamuraiId“ ist z. B. ein Integerwert, der keine NULL-Werte zulässt. Wenn Sie versuchen, ein Entrance-Objekt ohne einen mit Daten aufgefüllten SamuraiId-Wert einzufügen, fängt EF Core die ungültigen Daten nicht ab. Dies bedeutet auch, dass sich der InMemory-Anbieter zurzeit nicht beschwert. Ihre relationale Datenbank sollte aber einen Fehler für den Einschränkungskonflikt auslösen.

Aus einer DDD-Perspektive stellt dies aber nicht wirklich ein Problem dar, weil Sie sich nicht auf die Persistenzschicht verlassen sollten, um auf Fehler in der Domänenprogrammlogik hinzuweisen. Wenn „Samurai“ ein Entrance-Objekt erfordert, stellt dies eine Geschäftsregel dar. Wenn keine verwaisten Entrance-Objekte vorhanden sein dürfen, ist dies ebenfalls eine Geschäftsregel. Die Überprüfung sollte also sowieso Bestandteil Ihrer Domänenprogrammlogik sein.

Das folgende Beispiel zeigt einen der Grenzfälle, die ich bereits weiter oben erwähnt habe. Wenn der Fremdschlüssel in der abhängigen Entität (z. B. „Entrance“) nicht der Konvention folgt, können Sie die Fluent-API verwenden, um EF Core darüber zu informieren. Wenn „Entrance.SamuraiId“ z. B. den Wert „Entrance.SamuraiFK“ aufweist, können Sie „FK“ wie folgt abklären:

modelBuilder.Entity<Samurai>().HasOne(s=>s.Entrance)
  .WithOne().HasForeignKey<Entrance>(e=>e.SamuraiFK);

Wenn die Beziehung an beiden Enden erforderlich ist („Entrance“ also einen „Samurai“ aufweisen muss), können Sie „IsRequired“ hinter „WithOne“ hinzufügen.

Eigenschaften können weiter gekapselt werden

DDD ermöglicht das Erstellen von Aggregaten (Objektgraphen), bei denen der Aggregatstamm (das primäre Objekt im Graphen) alle anderen Objekte im Graphen steuert. Dies bedeutet das Schreiben von Code, der verhindert, dass anderer Code die Regeln falsch verwendet oder sogar missbraucht. Das Kapseln von Eigenschaften, damit diese nicht zufällig festgelegt (und häufig auch zufällig gelesen) werden können, ist eine Schlüsselmethode zum Schützen eines Graphen. In EF6 oder früher war es immer möglich, für Skalar- und Navigationseigenschaften private Setter festzulegen. Diese wurden von EF beim Lesen und Aktualisieren von Daten trotzdem erkannt. Diese Eigenschaften konnten aber nicht auf einfache Weise als privat deklariert werden. Ein Beitrag von Rowan Miller zeigt eine Möglichkeit, dies in EF6 zu erreichen, und verweist auf Links zu einigen früheren Problemumgehungen (bit.ly/2eHTm2t). Außerdem bestand keine echte Möglichkeit, eine Navigationssammlung in einer 1:n-Beziehung zu schützen. Über dieses letztgenannte Problem wurde viel geschrieben. Jetzt ist es nicht nur auf einfache Weise möglich, in EF Core private Eigenschaften zu verwenden, die Unterstützungsfelder (oder abgeleitete Unterstützungsfelder) aufweisen, sondern Sie können dank der Unterstützung für die Zuordnung von „IEnumerable<T>“ auch eine echte Kapselung von Sammlungseigenschaften erreichen. Ich habe mich mit Unterstützungsfeldern und „IEnumerable<T>“ in der bereits erwähnten Kolumne im Januar 2017 befasst und erspare mir daher hier die Details. Beide Aspekte sind aber für DDD-Muster sehr wichtig und sollten daher in diesem Artikel erwähnt werden.

Während Sie Skalare und Sammlungen verbergen können, möchten Sie einen anderen Typ von Eigenschaft wahrscheinlich kapseln: Navigationseigenschaften. Navigationseigenschaften profitieren von der Unterstützung von „IEnumerable<T>“. Private Navigationseigenschaften wie z. B. „Samurai.Entrance“ werden vom Modell aber nicht verstanden. Es besteht jedoch eine Möglichkeit, das Modell so zu konfigurieren, dass es eine Navigationseigenschaft versteht, die im Aggregatstamm verborgen ist.

Im folgenden Code deklariere ich „Entrance“ z. B. als eine private Eigenschaft von „Samurai“ (und ich verwende nicht einmal ein explizites Unterstützungsfeld, obwohl dies bei Bedarf natürlich möglich wäre). Sie können ein neues Entrance-Objekt mit der CreateEntrance-Methode erstellen (die eine Factorymethode in „Entrance“ aufruft), und Sie können die Eigenschaft „SceneName“ eines Entrance-Objekts nur lesen. Beachten Sie, dass ich den NULL-bedingten Operator von C# 6 verwende, um eine Ausnahme zu verhindern, wenn „Entrance“ noch nicht geladen wurde:

private Entrance Entrance {get;set;}
public void CreateEntrance (string sceneName) {
    Entrance = Entrance.Create (sceneName);
  }
public string EntranceScene => Entrance?.SceneName;

Gemäß der Konvention trifft EF Core keine Annahme zu dieser privaten Eigenschaft. Selbst wenn ich das Unterstützungsfeld verwenden würde, würde das private Entrance-Objekt nicht automatisch erkannt, und Sie wären nicht in der Lage, es bei der Interaktion mit dem Datenspeicher zu verwenden. Dieser API-Entwurf ist beabsichtigt, um Sie vor potenziellen Nebenwirkungen zu schützen. Die Konfiguration kann aber explizit erfolgen. Denken Sie daran, dass EF Core die 1:1-Beziehung verstehen kann, wenn „Entrance“ öffentlich ist. Da es sich aber um ein privates Objekt handelt, müssen Sie zuerst sicherstellen, dass es EF bekannt ist.

In „OnModelCreating“ müssen Sie die HasOne/WithOne-Fluentzuordnung hinzufügen, damit EF Core das Objekt erkennt. Da „Entrance“ privat ist, können Sie keinen Lambdaausdruck als Parameter für „HasOne“ verwenden. Stattdessen müssen Sie die Eigenschaft anhand ihres Typs und Namens beschreiben. „WithOne“ nimmt normalerweise einen Lambdaausdruck an, um die Navigationseigenschaft für das andere Ende des Paars anzugeben. Aber „Entrance“ verfügt über keine Samurai-Navigationseigenschaft, sondern nur über den Fremdschlüssel. Das ist in Ordnung! Sie können den Parameter leer lassen, weil EF Core nun über ausreichende Informationen verfügt, um die Zusammenhänge zu erschließen:

modelBuilder.Entity<Samurai> ()
  .HasOne (typeof (Entrance), "Entrance").WithOne();

Was geschieht, wenn Sie eine Unterstützungseigenschaft wie „_entrance“ in der Samurai-Klasse (wie in den folgenden Änderungen gezeigt) verwenden?

private Entrance _entrance;
private Entrance Entrance { get{return _entrance;} }
public void CreateEntrance (string sceneName) {
    _entrance = _entrance.Create (sceneName);
  }
public string EntranceScene => _entrance?.SceneName;

EF Core versteht, dass das Unterstützungsfeld beim Materialisieren der Entrance-Eigenschaft verwendet werden muss. In dem sehr langen Gespräch über GitHub, das ich mit Arthur Vickers während der Recherche zu diesem Artikel geführt habe, nannte er dafür den folgenden Grund: „Wenn ein Unterstützungsfeld, aber kein Setter vorhanden ist, verwendet EF das Unterstützungsfeld, [weil] nichts anderes verfügbar ist, das verwendet werden könnte“. Es funktioniert also einfach.

Wenn der Name dieses Unterstützungsfelds nicht der Konvention folgt (Sie es z. B. „_foo“ genannt haben), benötigen Sie eine Metadatenkonfiguration:

modelBuilder.Entity<Samurai> ()
  .Metadata
  .FindNavigation ("Entrance")
  .SetField("_foo");

Nun sind Aktualisierungen der Datenbank und Abfragen in der Lage, diese Beziehung zu erkennen. Wenn Sie Eager Loading verwenden möchten, sollten Sie daran denken, dass eine Zeichenfolge für „Entrance“ verwendet werden muss, weil das Objekt nicht vom Lambdaausdruck erkannt werden kann. Das folgende Beispiel zeigt dies:

var samurai = context.Samurais.Include("Entrance").FirstOrDefault();

Sie können die Standardsyntax für die Interaktion mit Unterstützungsfeldern (z. B. für Filter) verwenden. Dies wird im unteren Teil der Dokumentationsseite zu Unterstützungsfeldern unter bit.ly/2wJeHQ7 gezeigt.

Wertobjekte werden nun unterstützt

Wertobjekte sind ein wichtiges Konzept für DDD, weil sie das Definieren von Domänenmodellen als Werttypen ermöglichen. Ein Wertobjekt besitzt keine eigene Identität und wird zum Bestandteil der Entität, die es als Eigenschaft verwendet. Sehen Sie sich z. B. den Werttyp „string“ an, der aus einer Reihe von Zeichen besteht. Weil das Ändern auch nur eines Zeichens die Bedeutung des Worts ändert, sind Zeichenfolgen unveränderlich. Um eine Zeichenfolge zu ändern, müssen Sie das gesamte string-Objekt ersetzen. Die DDD-Empfehlung lautet, überall dort Wertobjekte zu verwenden, wo eine 1:1-Beziehung identifiziert wurde. Weitere Informationen zu Wertobjekten finden Sie im Kurs zu den DDD-Grundlagen, den ich weiter oben erwähnt habe.

EF hat schon immer die Möglichkeit unterstützt, Wertobjekte mithilfe des ComplexType-Typs einzuschließen. Sie könnten einen Typ ohne Schlüssel definieren und diesen Typ dann als Eigenschaft einer Entität verwenden. Das würde ausreichen, damit EF die Entität als „ComplexType“ erkennt und ihre Eigenschaften in der Tabelle zuordnet, der die Entität zugeordnet ist. Anschließend könnten Sie den Typ so erweitern, dass er auch über Features verfügt, die für ein Wertobjekt erforderlich sind. Solche Features sind z. B. das Sicherstellen, dass der Typ unveränderbar ist, und ein Verfahren zum Bewerten jeder Eigenschaft, wenn die Gleichheit bestimmt und der Hashwert überschrieben wird. Ich leite meine Typen häufig aus der ValueObject-Basisklasse von Jimmy Bogard ab, um diese Attribute schnell zu übernehmen.

Der Name einer Person ist ein Typ, der häufig als Wertobjekt verwendet wird. Sie können sicherstellen, dass immer eine allgemeine Regelsammlung beachtet wird, wenn der Name einer Person in einer Entität vorhanden sein soll. Abbildung 1 zeigt eine einfache PersonName-Klasse mit den Eigenschaften „First“ und „Last“ (beide vollständig gekapselt) sowie mit einer Eigenschaft zum Zurückgeben von „FullName“. Die Klasse soll sicherstellen, dass immer beide Bestandteile des Namens bereitgestellt werden.

Abbildung 1: Das PersonName-Wertobjekt

public class PersonName : ValueObject<PersonName> {
  public static PersonName Create (string first, string last) {
    return new PersonName (first, last);
  }
  private PersonName () { } 
  private PersonName (string first, string last) {
    First = first;
    Last = last;
  }
  public string First { get; private set; }
  public string Last { get; private set; }
  public string FullName => First + " " + Last;
}

Ich kann „PersonName“ als eine Eigenschaft in anderen Typen verwenden und zusätzliche Programmlogik in der PersonName-Klasse ausarbeiten. Der Vorteil eines Wertobjekts über einer 1:1-Beziehung besteht hier darin, dass ich die Beziehung nicht aufrechterhalten muss, wenn ich Code schreibe. Das ist objektorientierte Standardprogrammierung. Es ist einfach nur eine weitere Eigenschaft. In der Samurai-Klasse habe ich eine neue Eigenschaft dieses Typs hinzugefügt, ihren Setter als privat festgelegt und eine weitere Methode namens „Identify“ bereitgestellt, die anstelle des Setters verwendet werden soll:

 

public PersonName SecretIdentity{get;private set;}
public void Identify (string first, string last) {
  SecretIdentity = PersonName.Create (first, last);
}

Bis zu EF Core 2.0 war kein Feature verfügbar, das „ComplexTypes“ ähnelte. Daher konnten Wertobjekte nicht problemlos verwendet werden, ohne ein separates Datenmodell hinzuzufügen. Anstatt den „ComplexType“ in EF Core einfach erneut zu implementieren, hat das EF-Team ein Konzept erschaffen, das als „Entitäten im Besitz“ bezeichnet wird. Dieses Konzept nutzt ein weiteres Feature von EF Core: Schatteneigenschaften. Nun werden Eigenschaften im Besitz als zusätzliche Eigenschaften der Typen erkannt, die ihre Besitzer sind, und EF Core versteht, wie sie im Datenbankschema aufgelöst und wie Abfragen und Aktualisierungen erstellt werden, die diese Daten berücksichtigen.

Die EF Core 2.0-Konvention erkennt nicht automatisch, dass diese neue SecretIdentity-Eigenschaft ein Typ ist, der in die persistent gespeicherten Daten integriert werden soll. Sie müssen „DbContext“ explizit informieren, dass es sich bei der Samurai.SecretIdentity-Eigenschaft um eine Eigenschaft im Besitz in „DbContext.OnModelCreating“ handelt. Sie verwenden dazu die OwnsOne-Methode:

protected override void OnModelCreating (ModelBuilder modelBuilder) {
  modelBuilder.Entity<Samurai>().OwnsOne(s => s.SecretIdentity);
}

Auf diese Weise werden die Eigenschaften von „PersonName“ gezwungen, als Eigenschaften von „Samurai“ aufgelöst zu werden. Ihr Code verwendet den Samurai.SecretIdentity-Typ und navigiert über diesen zu den Eigenschaften „First“ und „Last“. Diese beiden Eigenschaften werden in Spalten in der Samurais-Datenbanktabelle aufgelöst. Die EF Core-Konvention weist ihnen den Namen der Eigenschaft in Samurai („SecretIdentity“) und den Namen der Entitätseigenschaft im Besitz zu. Abbildung 2 zeigt dies.

Das Schema der Samurais-Tabelle einschließlich der Eigenschaften des Werts
Abbildung 2: Das Schema der Samurais-Tabelle einschließlich der Eigenschaften des Werts

Nun kann ich den geheimen Namen eines Samurais identifizieren und mit Code speichern, der dem folgenden ähnelt:

using (var context = new SamuraiContext()) {
  var samurai = new Samurai { Name = "HubbieSan" 
  samurai.Identify ("Late", "Todinner");
  context.Samurais.Add (samurai);
  context.SaveChanges ();
}

Im Datenspeicher wird „Late“ im Feld „SecretIdentity_First“ und „Todinner“ im Feld „SecretIdentity_Last“ persistent gespeichert.

Anschließend kann ich einfach eine Abfrage nach einem Samurai ausführen:

var samurai=context.Samurais .FirstOrDefaultAsync (s => s.Name == "HubbieSan")

EF Core geht davon aus, dass die sich ergebende Eigenschaft „SecretIdentity“ des Samurais mit Daten aufgefüllt wurde, und ich kann die Identität dann anzeigen, indem ich Folgendes anfordere:

samurai.SecretIdentity.FullName

EF Core erfordert, dass Eigenschaften, die Entitäten im Besitz sind, mit Daten aufgefüllt werden. Im Beispieldownload können Sie untersuchen, wie ich erreicht habe, dass der PersonName-Typ dies ermöglicht.

Einfache Klassen für einfache Beispiele

Ich habe Ihnen hier einfache Klassen vorgestellt, die einige der Kernkonzepte einer DDD-Implementierung auf minimale Weise nutzen, damit Sie erkennen können, wie EF Core auf diese Konstrukte reagiert. Sie haben gesehen, dass EF Core 2.0 in der Lage ist, unidirektionale 1:1-Beziehungen zu verstehen. EF Core 2.0 kann Daten aus Entitäten persistent speichern, in denen Skalar-, Navigations- und Sammlungseigenschaften vollständig gekapselt sind. Außerdem ist es möglich, Wertobjekte in Ihrem Domänenmodell zu verwenden und ebenfalls persistent zu speichern.

Für diesen Artikel habe ich die Klassen einfach gehalten und auf zusätzliche Programmlogik verzichtet, die die Entitäten und Wertobjekte mithilfe von DDD-Mustern ordnungsgemäßer einschränkt. Diese Einfachheit spiegelt sich im Downloadbeispiel wider, das Sie auch auf GitHub unter bit.ly/2tDRXwi finden. Dort finden Sie sowohl die einfache Version als auch den erweiterten Branch, in dem ich dieses Domänenmodell optimiert und einige zusätzliche DDD-Praktiken auf den Aggregatstamm („Samurai“), seine zugehörige Entität („Entrance“) und das Wertobjekt („PersonName“) angewendet habe, damit Sie sehen können, wie EF Core 2.0 einen realistischeren Ausdruck eines DDD-Aggregats verarbeitet. In einer späteren Kolumne werde ich mich mit den erweiterten Mustern beschäftigen, die in diesem Branch angewendet wurden.

Bitte denken Sie daran, dass ich eine Version von EF Core 2.0 verwende, die kurz vor der endgültigen Veröffentlichung erstellt wurde. Zwar ist der Großteil des hier beschriebenen Verhaltens bereits festgelegt, es besteht aber noch immer die Möglichkeit kleinerer Abweichungen bis zur Veröffentlichung von Version 2.0.0.


Julie Lerman ist Microsoft Regional Director, Microsoft MVP, Mentorin für das Softwareteam und Unternehmensberaterin. Sie lebt in den Bergen von Vermont. Sie hält bei User Groups und Konferenzen in der ganzen Welt Vorträge zum Thema Datenzugriff und zu anderen Themen. Julie Lerman führt unter thedatafarm.com/blog einen Blog. Sie ist die Autorin von „Programming Entity Framework“ sowie der Ausgaben „Code First“ und „DbContext“ (alle bei O’Reilly Media erschienen). Folgen Sie ihr auf Twitter: @julielerman, und sehen Sie sich ihre Pluralsight-Kurse unter juliel.me/PS-Videos an.


Diesen Artikel im MSDN Magazine-Forum diskutieren