Warum die größte Stärke von EF Core gleichzeitig sein größtes Problem ist
Das Versprechen, das zu gut klang
Entity Framework Core versprach etwas Verlockendes: Vergiss die Datenbank. Denk in Objekten, schreib C#, der Rest ergibt sich. Für viele .NET-Entwickler war das jahrelang der Standard. Und für viele ist es das immer noch.
Das Problem ist nicht, dass dieses Versprechen falsch war. Es hat funktioniert — für eine bestimmte Art von Anwendung, in einer bestimmten Phase. Das Problem ist, dass es sich als universelle Wahrheit etabliert hat.
Und genau da kippt es.
Die Designprinzipien hinter EF Core
EF Core entstand in einer Zeit, in der Enterprise-Architektur stark auf OOP, Abstraktion und Patterns setzte. Die Grundideen sind klar:
Persistence Ignorance — die Datenbank ist ein Implementierungsdetail. Entwickler arbeiten mit C#-Klassen, nicht mit Tabellen. Das Domain Model kennt keine Datenbank.
Unit of Work — der DbContext verwaltet alles: Tracking, Änderungen, Transaktionen. Ein Scope, ein Commit, fertig.
Change Tracking — EF merkt sich automatisch, was geladen und was verändert wurde. Kein manuelles SQL, kein explizites Update.
LINQ als Query-Sprache — Queries werden typsicher in C# geschrieben. Compile-time Sicherheit statt Runtime-Fehler.
Code First — die Datenbank entsteht aus Code. Migrationen versionieren das Schema. Code führt, Infrastruktur folgt.
Jedes einzelne Prinzip hat eine Berechtigung. In Kombination entsteht aber ein System, das mehr versteckt als es zeigt.
Was davon heute noch trägt
Nicht alles an EF Core ist überholt. Ein Teil der Designentscheidungen ist nach wie vor sinnvoll.
LINQ und Typisierung bleiben ein echter Vorteil. Weniger Runtime-Fehler, gute IDE-Unterstützung, klare Ausdruckskraft. Das nutzen auch moderne Tools.
Code First und Migrationen sind inzwischen Standard — nicht nur in .NET, sondern auch in Prisma (JavaScript), Django ORM (Python) und Rails. Schema-Versionierung aus dem Code heraus hat sich bewährt.
Schneller Einstieg — für interne Tools, klassische Business-Apps und kleine bis mittlere Systeme ist EF Core nach wie vor produktiv. Setup in Minuten, CRUD funktioniert sofort.
Unit of Work in moderater Form — für einfache Schreiboperationen, bei denen ein Objekt geladen, verändert und gespeichert wird, ist das Pattern nach wie vor angemessen.
Wo es bröckelt
Hier wird es interessant. Denn die Probleme kommen nicht aus Bugs oder fehlenden Features — sie kommen aus dem Paradigma selbst.
Die Datenbank ist kein Detail
Das war der zentrale Denkfehler. Heute wissen wir: Die Datenbank ist nicht irgendein austauschbares Detail am Rand der Architektur. Sie ist das Zentrum. Performance hängt von Indizes ab, von Query-Plänen, von Datenvolumen. EF versteckt genau das.
Entwickler, die jahrelang mit EF arbeiten, verstehen oft kein SQL mehr. Nicht weil sie es nicht können, sondern weil sie es nie sehen. Das rächt sich bei der ersten echten Performance-Analyse.
Change Tracking ist teuer und intransparent
Change Tracking klingt komfortabel. In der Praxis bedeutet es: EF hält Kopien aller geladenen Objekte im Speicher, vergleicht sie bei SaveChanges und generiert daraus SQL. Bei großen Datenmengen wird das zum Problem — sowohl bei der Performance als auch beim Debugging.
Das Verhalten ist implizit. Wenn sich ein Objekt „magisch” ändert, weil irgendwo im Code eine Property gesetzt wurde, ist das schwer nachzuvollziehen. Moderne Entwicklung geht eher in Richtung: Explicit over implicit.
LINQ ist nicht SQL
LINQ wird in SQL übersetzt — aber nicht immer optimal. Edge Cases erzeugen ineffiziente Queries, N+1 Probleme sind klassisch, und das Debugging einer generierten Query ist deutlich aufwändiger als das Debugging einer geschriebenen.
// Sieht harmlos aus
var users = context.Users
.Where(u => u.IsActive)
.Include(u => u.Orders)
.ToList();
// Kann aber bedeuten: Full Table Scan + JOIN über Millionen Rows
Der Entwickler sieht eine Filteroperation auf einer Liste. Real passiert ein Netzwerk-Call, ein Query Planner entscheidet über den Execution Plan, I/O wird ausgelöst, Locks werden gesetzt.
Repository Pattern obendrauf
EF Core ist bereits ein Repository. DbSet<T> ist das Repository, DbContext ist die Unit of Work. Trotzdem bauen viele Teams noch eine zusätzliche Repository-Schicht darüber — doppelte Abstraktion ohne Mehrwert. Heute arbeitet man eher direkt mit dem DbContext.
Der eigentliche Paradigmenwechsel
Früher dachte man: Ich arbeite mit Objekten — die Datenbank ist nur ein Detail.
Heute: Ich arbeite mit Daten — und die Datenbank ist das Zentrum.
Das klingt nach einem kleinen Unterschied. In der Praxis verändert es alles.
Gute Entwickler fragen nicht mehr „Wie schreibe ich das in EF?” sondern „Wie muss die Query aussehen — und wie mappe ich das Ergebnis?”
Der Aufwand, der sich normal anfühlte
Mit Entity Framework hatte man immer diese Begleitkosten: Mapping pflegen, Navigation Properties managen, Includes orchestrieren, Change Tracking verstehen, Migrationen sauber halten, „komisches Verhalten” debuggen.
Das wurde akzeptiert, weil das Versprechen war: Danach wird alles einfacher.
Im Rückblick stellt sich heraus: Ein relevanter Teil dieses Aufwands kam nicht vom Problem, sondern vom Tool. Man hat Aufwand in Abstraktion investiert statt in Klarheit.
Mit EF gedacht: Ich modelliere mir ein Objektmodell, der Rest ergibt sich. Realität: Ich kämpfe mit Includes, Lazy vs. Eager Loading, Tracking und Performance-Problemen.
Für 70 bis 80 Prozent der Anwendungen war das Overengineering. Die ehrliche Antwort auf die Frage „Warum baue ich mir diese Abstraktionsschicht?” lautet oft: „Weil man es früher so gemacht hat.”
Moderne Gegenbewegungen
Micro-ORMs: SQL bleibt sichtbar
Dapper ist das bekannteste Beispiel. SQL wird direkt geschrieben, das Mapping ist minimal:
var users = connection.Query<User>(
"SELECT Id, Name FROM Users WHERE IsActive = 1"
);
Volle Kontrolle, maximale Performance, keine Magie. Der Preis: mehr Handarbeit bei komplexen Szenarien.
Query-first / SQL-first
SQL ist wieder First-Class Citizen. Tools wie Dapper (.NET), SQLC (Go) und Kysely (TypeScript) setzen darauf, dass die Query der Ausgangspunkt ist — nicht das Objektmodell.
CQRS und Vertical Slice Architecture
Trennung von Reads und Writes. Lesezugriffe werden query-optimiert (oft mit Dapper oder Raw SQL), Schreibzugriffe nutzen Domain-Logik (manchmal noch mit EF). Das Beste aus beiden Welten.
Functional und Explicit Data Access
Der Trend geht zu: keine versteckten Zustände, kein Tracking, pure Funktionen. Daten rein, Daten raus, nachvollziehbar.
Der Hybrid ist heute Standard
Wenn man EF Core nutzt, dann bewusst und eingeschränkt:
- Tracking deaktivieren wo möglich (
AsNoTracking) - Explizite Queries statt magischer Includes
- SQL verstehen — auch wenn EF es generiert
- Kein unnötiges Repository Pattern obendrauf
- Für kritische Queries Dapper oder Raw SQL mischen
EF Core für Writes, Dapper für Reads — das ist kein Kompromiss, sondern eine pragmatische Architekturentscheidung.
Wann EF Core noch passt — und wann nicht
Sinnvoll:
- Klassische CRUD-Anwendungen
- Interne Tools und Admin-Panels
- Kleine bis mittlere Systeme
- Teams ohne tiefen SQL-Fokus
Problematisch:
- High-Performance-Systeme
- Komplexe Queries und Reporting
- Analytics und große Datenmengen
- Systeme, bei denen jede Millisekunde zählt
Der Selbsttest
Nimm eine echte Query aus deinem Projekt und frag dich:
- Wie sieht das generierte SQL aus?
- Welche Indizes werden genutzt?
- Wie viele Rows gehen über die Leitung?
Wenn du das nicht beantworten kannst, bist du noch im EF-Denken gefangen. Das ist kein Vorwurf — es ist der Punkt, an dem der Paradigmenwechsel beginnt.
Einordnung
EF Core ist kein falsches Tool. Aber es stammt aus einer Zeit, in der man Datenbanken verstecken wollte, viel Magie akzeptiert hat und stark auf OOP gesetzt hat.
Heute geht die Entwicklung in eine andere Richtung: Transparenz statt Abstraktion, Kontrolle statt Komfort, SQL wieder ernst nehmen.
Der eigentliche Paradigmenwechsel in einem Satz:
Von „Objekte speichern” zu „Daten gezielt abfragen.”
Wer das verstanden hat, nutzt EF Core anders — oder gar nicht mehr. Beides ist legitim. Aber die Entscheidung sollte bewusst fallen, nicht aus Gewohnheit.