August 2016

Band 31, Nummer 8

Datenpunkte: EF Core – Verhalten bei der Änderungsnachverfolgung: Unchanged, Modified und Added

Von Julie Lerman

Julie LermanHaben Sie gesehen, was ich im Titel dieser Kolumne gemacht habe? Sie haben vielleicht „Unchanged“ (Unverändert), „Modified“ (Geändert) und „Added“ (Hinzugefügt) als Enumerationen für „Entity­State“ in Entity Framework (EF) erkannt. Diese Angaben helfen mir auch beim Beschreiben der Verhalten bei der Änderungsnachverfolgung in EF Core im Vergleich mit früheren Versionen von Entity Framework. Die Änderungsnachverfolgung ist in EF Core einheitlicher geworden, weshalb Sie eher wissen können, was Sie erwartet, wenn Sie mit getrennten Daten arbeiten.

Wichtig ist der Hinweis, dass obwohl EF Core versucht, die Muster und einen Großteil der Syntax früherer Entity Framework-Versionen beizubehalten, EF Core eine neue Gruppe von APIs darstellt, d. h. eine von Grund auf völlig neu geschriebene Codebasis. Deshalb ist es wichtig, nicht davon auszugehen, dass sich alles exakt wie bislang verhält. Die Änderungsprotokollierung ist dabei ein wichtiger Punkt.

Da die erste Iteration von EF Core auf eine Angleichung an ASP.NET Core abzielt, lag ein großer Schwerpunkt der Arbeit auf dem getrennten Status. Ziel ist es daher sicherzustellen, dass Entity Framework den Status von Objekten verarbeiten kann, die sozusagen außer der Reihe kommen und bislang von EF nicht nachverfolgt wurden. Ein typisches Szenario ist eine Web-API, die Objekte von einer Clientanwendung akzeptiert, die persistent in der Datenbank gespeichert werden sollen.

In meiner Kolumne „Datenpunkte“ vom März 2016 habe ich das „Behandeln des Status getrennter Entitäten in EF“ (msdn.com/magazine/mt694083) beschrieben. In diesem Artikel habe ich mich mit dem Zuweisen von Statusinformationen zu getrennten Entitäten und dem gemeinsamen Nutzen dieser Informationen mit EF beschäftigt, wenn diese Objekte an die Änderungsprotokollierung in EF zurückgegeben werden. Wenngleich ich EF6 zum Gestalten des Beispiels verwendet habe, ist dieses Muster für EF Core weiterhin relevant. Nach der Erläuterung der EF Core-Verhalten zeige ich deshalb ein Beispiel, wie dieses Muster in EF Core implementiert wird.

Nachverfolgung mit DbSet: Geändert

DbSet ist in den Methoden „Add“, „Attach“ und „Remove“ stets enthalten. Das Ergebnis dieser Methoden für ein einzelnes Objekt ist recht einfach, denn sie legen den Status des Objekts auf den entsprechenden „EntityState“ fest. „Add“ resultiert in „Added“, „Attach“ in „Unchanged“, und „Remove“ ändert den Status in „Deleted“. Es gibt eine Ausnahme: Wenn Sie eine Entität haben, die bereits als „Added“ bekannt ist, wird diese von „DbContext“ getrennt, da es nicht mehr nötig ist, die neue Entität nachzuverfolgen.  Wenn Sie in EF6 diese Methoden mit Graphen nutzen, sind die Auswirkungen auf die dazugehörigen Objekte nicht ganz so konsistent. Ein zuvor nicht nachverfolgtes Objekt kann nicht entfernt werden und würde einen Fehler auslösen. Bei bereits nachverfolgten Objekten ändert sich der Status ggf., was davon abhängt, was diese Status sind. Ich habe in EF6 eine Reihe von Tests erstellt, die Sie auf GitHub unter bit.ly/28YvwYd finden, um die verschiedenen Verhalten zu verstehen.

Bei der Entwicklung von EF Core hat das EF-Team in allen Betaversionen mit dem Verhalten dieser Methoden experimentiert. In EF Core RTM verhalten sich die Methoden nicht mehr wie in EF6 und früher. In den meisten Fällen führen die Änderungen an diesen Methoden zu einem konsistenteren Verhalten, auf das Sie sich verlassen können. Doch wichtig ist es zu verstehen, wie sie sich geändert haben.

 Wenn Sie die Methoden „Add“, „Attach“ und „Remove“ mit einem Objekt verwenden, an das ein Graph angefügt ist, wird der Status jedes Objekts im Graph, der der Änderungsprotokollierung unbekannt ist, auf den von der Methode bestimmten Status festgelegt. Lassen Sie mich dies mithilfe meines bevorzugten EF Core-Modells aus dem Film „Die sieben Samurai“ erläutern, d. h. Samurais mit zugewiesenen Filmzitaten und anderen zugehörigen Informationen.

Wenn ein Samurai neu ist und nicht nachverfolgt wird, legt „Samurais.Add“ den Status dieses Samurais auf „Added“ fest. Wenn diesem Samurai ein Zitat zugewiesen ist und „Add“ aufgerufen wird, wird dessen Status ebenfalls auf „Added“ festgelegt. Dies ist das gewünschte Verhalten, das zudem mit dem in EF6 identisch ist.

Was wäre, wenn Sie einem vorhandenen Samurai ein neues Zitat hinzufügen und anstatt meiner Empfehlung zu folgen, „newQuote.SamuraiId“ auf den Wert „Samurai.Id“ festzulegen, Sie stattdessen die Navigationseigenschaft „newQuote.Samurai=oldSamurai“ festlegen? In einem Szenario mit getrennten Daten, in dem weder das Zitat noch „oldSamurai“ von EF nachverfolgt werden, macht „Quotes.Add(newQuote)“ dasselbe wie der Vorgänger. „newQuote“ wird als „Added“ und auch „oldSamurai“ wird als „Added“ markiert. Bei Ausführen von „SaveChanges“ werden beide Objekte in die Datenbank eingefügt, sodass „oldSamurai“ in der Datenbank doppelt vorhanden ist.

Wenn Sie sich in einer Clientanwendung befinden, z. B. Windows Presentation Foundation (WPF), und Sie Ihren Kontext zum Abfragen der Samurais und dann dieselbe Kontextinstanz zum Aufrufen von „context.Quotes.Add(newQuote)“ verwenden, kennt der Kontext bereits den „oldSamurai“, sodass dessen „Unchanged“-Status nicht in „Added“ geändert wird. Das ist es, was ich mit „Nicht ändern des Status bereits nachverfolgter Objekte“ meine.

Die Weise, in der die Änderungsprotokollierung sich auf verwandte Objekte in einem getrennten Graph auswirkt, unterscheidet sich deutlich, und Sie müssen diese Unterschiede bei Verwenden dieser Methoden in EF Core berücksichtigen.

Rowan Miller hat dieses neue Verhalten in einem auf diese Problematik bezogenen GitHub-Artikel (bit.ly/295goxw) zusammengefasst:

Add: Fügt jede erreichbare Entität hinzu, die nicht bereits nachverfolgt wird.

Attach: Fügt jede erreichbare Entität an, es sei denn, eine erreichbare Entität hat einen vom Speicher generierten Schlüssel und kein Schlüsselwert ist zugewiesen. Diese werden als „Added“ markiert.

Update: Entspricht „Attach“, doch Entitäten werden als „Modified“ markiert.

Remove: Entspricht „Attach“, und der Stamm wird als „Deleted“ markiert. Da für „SaveChanges“ ein kaskadierendes DELETE erfolgt, wird dadurch das Übergeben kaskadierender Regeln im weiteren Verlauf ermöglicht.

In dieser Liste gibt es eine weitere Änderung an den „DbSet“-Methoden, die Ihnen ggf. auffällt: „DbSet“ hat endlich eine „Update“-Methode, die den Status nicht nachverfolgter Objekte auf „Modified“ festlegt. Hurra! Was für eine bequeme Alternative zum vorherigen stets erforderlichen Aufrufen von „Add“ oder „Attach“ und dem anschließenden expliziten Festlegen des Status auf „Modified“.

„Range“-Methoden für DbSet: Ebenfalls geändert

In EF6 wurden zwei „Range“-Methoden („AddRange“ und „RemoveRange“) eingeführt, die das Übergeben eines Arrays ähnlicher Typen ermöglichen. Dies sorgte für eine enorme Leistungssteigerung, da die Änderungsprotokollierung nur einmal statt bei jedem Element im Array ausgelöst wird. Die Methoden rufen, wie zuvor beschrieben, „Add“ und „Remove“ auf, weshalb Sie prüfen müssen, wie verbundene Graphobjekte davon betroffen sind.

In EF6 gab es die „Range“-Methoden nur für „Add“ und „Remove“, doch EF Core bietet nun auch „UpdateRange“ und „AttachRange“. Die Methoden „Update“ und „Attach“, die für jedes Objekt bzw. jeden Graph einzeln aufgerufen und an die „Range“-Methoden übergeben werden, verhalten sich wie zuvor beschrieben.

Änderungsnachverfolgungsmethoden für „DbContext“: Hinzugefügt

Wenn Sie vor der Einführung von „DbContext“ in EF mit „ObjectContext“ gearbeitet haben, erinnern Sie sich ggf., dass es für „ObjectContext“ die Methoden „Add“, „Attach“ und „Delete“ gab. Da der Kontext nicht wissen konnte, zu welchem „ObjectSet“ die Zielentität gehörte, mussten Sie dem „ObjectSet“-Namen eine Zeichenfolgendarstellung als Parameter hinzufügen. Das war sehr unschön, weshalb viele von uns es einfacher fanden, nur die „ObjectSet“-Methoden „Add“, „Attach“ und „Delete“ zu verwenden. Nachdem „DbContext“ eingeführt wurde, verschwanden diese unschönen Methoden, sodass über „DbSet“ nur „Add“, „Attach“ und „Remove“ verwendet werden konnten.

In EF Core sind die Methoden „Add“, „Attach“ und „Remove“ als Methoden für „DbContext“ wieder zurück. Außerdem gibt es jetzt die „Update“-Methode und die vier verwandten „Range“-Methoden („AddRange“ usw.). Doch diese Methoden sind jetzt wesentlich eleganter. Denn Sie können nun den Typ bestimmen und die Entität automatisch in Bezug zum richtigen „DbSet“ setzen. Das ist wirklich praktisch, da Sie generischen Code schreiben können, ohne ein „DbSet“ instanziieren zu müssen. Der Code ist einfacher und, was noch wichtiger ist, leichter auffindbar. Hier ein Vergleich von EF6 und EF Core:

private void AddToSetEF6<T>(T entity) where T : class {Pull
  using (var context = new SamuraiContext()) {
    context.Set<T>().Add(entity);
  }
}
private void AddToSetEFCore(object entity) {
  using (var context = new SamuraiContext()) {
    context.Add(entity);
   }
}

Die „Range“-Methoden sind noch hilfreicher, da Sie an sie eine Vielzahl von Typen übergeben können, die EF bestimmen kann:

private void AddViaContextEFCore(object[] entitiesOfVaryingTypes) {
  using (var context = new SamuraiContext()) {
     context.AddRange(entitiesOfVaryingTypes);
  }
}

DbContext.Entry: Geändert – Unbedingt das veränderte Verhalten beachten

Obgleich wir gewarnt wurden, dass EF Core nicht EF6 ist und wir nicht erwarten sollten, dass vertrauter Code sich genauso wie in EF6 verhält, ist es dennoch schwierig, eine solche Erwartung aufrechtzuerhalten, wenn so viele Verhalten unverändert bleiben. „DbContext.Entry“ ist hingegen ein Paradebeispiel, bei dem Sie verstehen müssen, was sich geändert hat.

Von mir wird diese Änderung begrüßt, da sie für die Nachverfolgung von Änderungen Konsistenz bringt. In EF6 hatten die „DbSet“-Methoden „Add“ (usw.) und die „DbContext.Entry“-Methode in Kombination mit der „State“-Eigenschaft dieselbe Auswirkung auf Entitäten und Graphen. Bei Verwenden von „DbContext.Entry(object).State=EntityState.Added“ werden alle Objekte in einem Graph (die nicht bereits nachverfolgt wurden) als „Added“ markiert.

Darüber hinaus gab es überhaupt keine intuitive Möglichkeit, Graphobjekte zu trennen, ehe sie an die Änderungsprotokollierung übergeben wurden.

In EF Core wirkt sich „DbContext.Entry“ nur auf das Objekt aus, das übergeben wird. Falls mit diesem Objekt andere Objekte verbunden sind, werden diese von „DbContext.Entry“ ignoriert.

Wenn Sie gewöhnt sind, die „Entry“-Methode zum Verbinden von Graphen mit einer „DbContext“-Instanz zu verwenden, können Sie erkennen, warum diese Änderung drastisch ist. Sie bedeutet, dass Sie auf ein einzelnes Objekt abzielen können, auch wenn es Teil eines Graphs ist.

Noch wichtiger ist, dass Sie nun explizit die Nachverfolgungsmethoden von „DbSet“ und „DbContext“ („Add“ usw.) verwenden können, um explizit mit Graphen zu arbeiten. Die „DbContext.Entry“-Methode können Sie nutzen, um spezifisch mit einzelnen Objekten zu arbeiten. Dies in Kombination mit der nächsten Änderung, die ich erläutere, bedeutet, dass Sie nun aus klaren Optionen eine Auswahl treffen können, wenn Objektgraphen an die EF Core-Änderungsprotokollierung übergeben werden.

DbContext.ChangeTracker.TrackGraph: Hinzugefügt

„TrackGraph“ ist ein völlig neues Konzept in EF Core. Es bietet die ultimative Kontrolle über jedes Objekt in einem Objektgraph, mit dessen Nachverfolgung Ihr „DbContext“ beginnen soll.

„TrackGraph“ durchläuft die einzelnen Objekte im Graph und wendet eine vorgesehene Funktion auf jedes dieser Objekte an. Die Funktion ist der zweite Parameter der „TrackGraph“-Methode.

Das gängigste Beispiel ist das Festlegen des Status jedes Objekts auf einen gemeinsamen Status. Im folgenden Code durchläuft „TrackGraph“ alle Objekte im neuen Graph „newSword“ und legt ihren Status auf „Added“ fest:

context.ChangeTracker.TrackGraph(newSword, e => e.Entry.State = EntityState.Added);

Derselbe Vorbehalt wie bei den Methoden „DbSet“ und „DbContext“ gilt auch für „TrackGraph“: Wenn die Entität bereits nachverfolgt wird, ignoriert „TrackGraph“ sie. Wenngleich sich diese besondere Nutzung von „TrackGraph“ wie bei den Nachverfolgungsmethoden „DbSet“ und „DbContext“ verhält, bietet sie dennoch mehr Möglichkeiten zum Schreiben von wiederverwendbarem Code:

Der Lambda-Ausdruck („e“ in diesem Code) stellt einen „EntityEntryGraphNode“-Typ dar. Der „EntityEntryGraphNode“-Typ macht auch eine Eigenschaft namens „NodeType“ verfügbar, auf die Sie ggf. beim Eingeben des Lambda-Ausdrucks per IntelliSense stoßen. Dieser scheint nur für die interne Verwendung vorgesehen zu sein und bietet nicht die Auswirkung von „e.Entry.State“, weshalb Sie ihn nicht unwillentlich verwenden sollten.

In einem Szenario mit getrennten Daten ist die Einschränkung, dass bereits nachverfolgte Objekte ignoriert werden, ggf. nicht relevant. Der Grund dafür ist, dass die „DbContext“-Instanz neu und leer ist, sodass alle Graphobjekte für „DbContext“ neu sein müssen. Erwägen Sie jedoch die Möglichkeit der Übergabe einer Sammlung von Graphen in eine Web-API. Nun besteht die Möglichkeit mehrerer Verweise auf ein gemeinsames Objekt. Die Änderungsprotokollierung von EF prüft die Identität, um zu bestimmen, ob eine Entität bereits nachverfolgt wird. Dies ist ein überzeugendes Argument, das Objekt nicht ein zweites Mal der Änderungsprotokollierung hinzuzufügen.

Dieses Standardverhalten ist so ausgelegt, dass die gängigsten Szenarien abgedeckt sind. Doch ich kann mir vorstellen, dass Sie wie ich bereits an Grenzfälle denken, bei denen dieses Muster ggf. nicht funktioniert.

An dieser Stelle greife ich auf meinen Artikel vom März 2016 und das Muster zurück, das ich zum Festlegen des Objektstatus für Ihre Klassen und anschließende Lesen dieses Status genannt habe, um die Änderungsprotokollierung zu informieren, was der „Entity­State“ des Objekts sein soll. Jetzt kann ich dieses Muster und die „TrackGraph“-Methode kombinieren, indem ich die „TrackGraph“-Aufrufe der Funktion die Festlegung des „EntityState“ basierend auf der „State“-Methode des Objekts ausführen lasse.

Die Aufgaben in den Domänenklassen haben sich im Vergleich zum Artikel vom März nicht geändert. Ich beginne mit der Definition einer Enumeration für den lokal nachverfolgten „ObjectState“:

public enum ObjectState {
    Unchanged,
    Added,
    Modified,
    Deleted
  }

Dann erstelle ich eine „IEntityObjectWithState“-Schnittstelle, die basierend auf der Enumeration eine „State“-Eigenschaft verfügbar macht:

public interface IObjectWithState
{
  ObjectState State { get; set; }
}

Nun richte ich meine Klassen so ein, dass die Schnittstelle implementiert wird. Als Beispiel folgt hier eine kleine Klasse aus „Location“ mit vorhandener Schnittstelle:

using SamuraiTracker.Domain.Enums;
using SamuraiTracker.Domain.Interfaces;
namespace SamuraiTracker.Domain
{
  public class Location : IObjectWithState
  {
    public int Id { get; set; }
    public string Name { get; set; }
    public ObjectState State { get; set; }
  }
}

Im Artikel vom März habe ich gezeigt, wie intelligente Klassen erstellt werden, die ihren lokalen Status verwalten können. Ich habe dies bei diesem Beispiel nicht wiederholt, was heißt, dass ich den Setter öffentlich gelassen habe und diesen Status manuell festlegen muss. In einer konkretisierten Lösung muss ich diese Klassen straffen, damit sie mehr denen im früheren Artikel ähneln.

Für „DbContext“ verfüge ich über einige statische Methoden in einer Hilfsklasse namens „ChangeTrackerHelpers“ (siehe Abbildung 1).

Abbildung 1: Die Hilfsklasse „ChangeTrackerHelpers“

public static class ChangeTrackerHelpers
    {
    public static void ConvertStateOfNode(EntityEntryGraphNode node) {
      IObjectWithState entity = (IObjectWithState)node.Entry.Entity;
      node.Entry.State = ConvertToEFState(entity.State);
    }
    private static EntityState ConvertToEFState(ObjectState objectState) {
      EntityState efState = EntityState.Unchanged;
      switch (objectState) {
        case ObjectState.Added:
          efState = EntityState.Added;
          break;
        case ObjectState.Modified:
          efState = EntityState.Modified;
          break;
        case ObjectState.Deleted:
          efState = EntityState.Deleted;
          break;
        case ObjectState.Unchanged:
          efState = EntityState.Unchanged;
          break;
      }
      return efState;
    }
  }

„ConvertStateOfNode“ ist die von „TrackGraph“ aufgerufene Methode. Sie legt den „EntityState“ des Objekts auf einen von der „ConvertToEFState“-Methode bestimmten Wert fest, der den „IObjectWithState.State“-Wert in einen „EntityState“-Wert umwandelt.

Nun kann ich beginnen, mithilfe von „TrackGraph“ meine Objekte sowie deren ordnungsgemäß zugewiesenen „EntityStates“ nachzuverfolgen. Hier ist ein Beispiel, bei dem ich einen Objektgraph namens „samurai“ übergebe, der aus einem Samurai mit dazugehörigem „Quote“ und „Sword“ besteht:

context.ChangeTracker.TrackGraph(samurai, ChangeTrackerHelpers.ConvertStateOfNode);

Bei der EF6-Lösung musste ich die Elemente der Änderungsprotokollierung hinzufügen und dann explizit eine Methode aufrufen, die alle Einträge in der Änderungsprotokollierung durchlief, um den entsprechenden Status jedes Objekts festzulegen. Die EF Core-Lösung ist wesentlich effizienter. Beachten Sie, dass ich noch nicht die möglichen Auswirkungen auf die Leistung beim Verarbeiten großer Datenmengen in einer einzelnen Transaktion untersucht habe.

Wenn Sie den Beispielcode zu dieser Kolumne herunterladen, werden Sie sehen, dass ich dieses neue Muster in einem Integrationstest namens „Can­ApplyStateViaChangeTracker“ befolge. Darin erstelle ich diesen Graph, weise den verschiedenen Objekten unterschiedliche Status zu und überprüfen dann, ob die resultierenden „EntityState“-Werte richtig sind.

IsKeySet: Hinzugefügt

Das „EntityEntry“-Objekt, das die Nachverfolgungsinformationen für jede Entität enthält, weist nun die neue Eigenschaft „IsKeySet“ auf. „IsKeySet“ ist eine großartige Ergänzung der API und prüft, ob die „Key“-Eigenschaft in der Entität einen Wert hat. Dadurch ist nicht mehr das Ratespiel (samt zugehörigem Code) erforderlich, um festzustellen, ob ein Objekt bereits einen Wert in seiner „key“-Eigenschaft (bzw. Eigenschaften, wenn Sie einen zusammensetzten Schlüssel haben) aufweist. „IsKeySet“ prüft, ob der Wert der Standardwert des bestimmten Typs ist, den Sie für die „key“-Eigenschaft angegeben haben. Wenn der Typ „int“ ist, entspricht der Wert 0? Wenn der Typ „Guid“ ist, entspricht der Wert „Guid.Empty (00000000-0000-0000-0000-000000000000)“? Wenn der Wert nicht der Standard für den Typ ist, gibt „IsKeySet“ TRUE zurück.

Wenn Sie wissen, dass Sie in Ihrem System ein neues Objekt zweifelsfrei von einem bereits vorhandenen Wert unterscheiden können, und zwar anhand des Werts seiner „key“-Eigenschaft, dann ist „IsKeySet“ eine wirklich praktische Eigenschaft für das Bestimmen des Status von Entitäten.

EF Core sehr aufmerksam nutzen

Wenngleich das EF-Team bestimmt sein Bestes getan hat, um Ihnen die Mühe des Übergangs von früheren Versionen von Entity Framework zu EF Core zu erleichtern und dafür einen Großteil der Syntax und Verhalten übernommen hat, darf nicht vergessen werden, dass es sich um unterschiedliche APIs handelt. Das Portieren von Code ist knifflig und wird nicht empfohlen, insbesondere zu Anfang, wenn die RTM-Version nur eine Teilmenge der vertrauten Features bietet. Doch selbst wenn Sie neue Projekte mit der Gewissheit angehen, dass das Featureangebot von EF Core genau das ist, was Sie brauchen, sollten Sie nicht davon ausgehen, dass die Dinge genau wie zuvor laufen. Ich muss mich selbst immer noch daran erinnern. Nichtsdestotrotz gefallen mir die Änderungen an der Änderungsprotokollierung, denn sie bringen mehr Klarheit, Konsistenz und Kontrolle beim Umgang mit getrennten Daten.

Das EF-Team hat einen Fahrplan auf der GitHub-Seite veröffentlicht, zu dem ich einen praktischen Link erstellt habe: bit.ly/efcoreroadmap. Dadurch können Sie die Features im Blick behalten, wenngleich detaillierte Aspekte wie Verhaltensänderungen nicht aufgeführt sind. Hierfür empfehle ich Tests und nochmals Tests, um sicherzustellen, dass alles funktioniert, wie Sie es erwarten. Wenn Sie vorhaben, Code aus früheren Versionen von EF zu portieren, sollten Sie sich die Website (approvaltests.com) von Llewellyn Falco ansehen, auf der Sie die Ausgabe von Tests vergleichen können, um sicherzustellen, dass die Ausgaben weiter übereinstimmen.


Julie Lerman ist Microsoft MVP, .NET-Mentorin 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 anderen .NET-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.

Unser Dank gilt dem folgenden technischen Experten bei Microsoft für die Durchsicht dieses Artikels: Erik Ejlskov Jensen
Erik Ejlskov Jensen ist .NET- und Datenbankentwickler bei NNIT A/S sowie ein Microsoft Data Platform MVP. Er bietet verschiedene kostenlose Open-Source-Tools- und Bibliotheken für .NET-Datenbankentwickler auf GitHub, so z. B. die beliebte Visual Studio-Erweiterung „SQLite & SQL Server Compact Toolbox“. Er hat auch einen Datenanbieter für Entity Framework Core entwickelt. Folgen Sie ihm auf Twitter: @ErikEJ, um bei der .NET-Datenzugriffsentwicklung auf dem Laufenden zu bleiben.