13 March 2009 ADO.NET EF; Robert Muehsig

image Der Einsatz von Linq2Sql oder der Einsatz vom ADO.NET Entity Framework ist sehr einfach - die Einstiegshürden sind erstmal niedrig und man bastelt seine Datenbank, generiert daraus sein Model und dann kann man schon loslegen und alles ist heile Welt. Der Titel hier ist etwas provokant, allerdings muss ich immer wieder erleben dass der Einsatz solcher Tools/Frameworks (es gibt sicherlich noch andere dieser Art) auch richtig schön schief gehen kann.

Erstmal langsam... es gibt ja auch gute Seiten
Ich bezieh mich hier nur auf Linq2Sql oder dem ADO.NET EF, sicherlich gibt es noch andere Beispiele wo es ähnlich ist. Beide Technologien sind eigentlich ganz nett, das EF wird sicherlich in Version 2 dann fortschritte machen und Linq2Sql ist halt sehr einfach.
Das tolle ist ja, dass ich mir keine Gedanken mehr um die SQL Code Generierung machen muss und (ich habe jetzt mal die Northwind DB genommen) auch alles schön da:

image

Und im Code hat man ebenfalls den kompletten Zugriff auf die Verzweigtesten Sachen (auch wenn das so kein Sinn macht) :

            Products test = new Products();
            test.Categories.CategoryName;
            test.Suppliers.Address;
            
            Customers cust = new Customers();
            cust.Orders.First().Order_Details.First().Products;

Durch den Context kann man auch jedes Teil entsprechen nachladen oder beim EF kann man über "Load" Daten nachladen, wenn diese noch nicht geladen wurden.

Die Probleme dabei...
1. Problem ist, dass mein Model aufgebläht ist mit Sachen, die mich nicht interessieren:

image

Darunter "EntityKey", "EntityState", Orders (was nützlich ist) und "OrdersReference" - wenn man diese Objekte nun z.B. nach JSON serialisieren will, kann man in arge Schwierigkeiten kommen.

2. Problem ist, dass man durch "Load()" oder durch den Einsatz des Contextes überall Daten laden kann:
Wenn man aus jeder Schicht (sei es Businesslogik oder UI Schicht) Daten nachladen kann, dann kommt am Ende sowas raus:

image

Das Problem kann man umgehen, wenn man stark darauf achtet, dass es nur eine Stelle gibt an der Daten geladen werden. So wird auch im MVC Buch von ScottGu etc. empfohlen:

"For small applications it is sometimes fine to have Controllers work directly against a LINQ to SQL DataContext class, and embed LINQ queries within the Controllers. As applications get larger, though, this approach becomes cumbersome to maintain and test. It can also lead to us duplicating the same
LINQ queries in multiple places.
One approach that can make applications easier to maintain and test is to use a “repository” pattern. A repository class helps encapsulate data querying and persistence logic, and abstracts away the implementation details of the data persistence from the application. In addition to making application
code cleaner, using a repository pattern can make it easier to change data storage implementations in the future, and it can help facilitate unit testing an application without requiring a real database."

3. Problem ist, dass es trotzdem sehr leicht ist die Architektur kaputt zu machen:
Selbst wenn man ein Repository Pattern implementiert, bleiben viele EF oder Linq2Sql Sachen am Objekt hängen und man kommt sehr leicht dazu, "einfach mal so" auf die DB zu gehen. In einem größeren Team kann das durchaus recht schnell passieren.

4. Problem ist, dass nicht alles mit dem Entity Framework / Linq2Sql abgedeckt werden kann:
Selbst wenn ich mit den vielen Objektreferenzen leben kann und auch wirklich sehr sauber Arbeite, habe ich ein Problem wenn man z.B. plötzlich ein Webservice mit einbindet. Plötzlich hat man ein Teil der Klassen durch das EF generiert und ein anderer Teil kommt irgendwo anders her und kann sich auch anders Verhalten.

5. Problem ist, dass man dadurch sich sehr schnell festlegt und später Probleme bekommt:
Auch wenn momentan das Entity Framework von Microsoft weiterentwickelt wird, heisst das ja nicht, dass es sich nicht in 2 Jahren wieder ändern könnte. Wenn man nun in seiner Applikation überall mit dem Context rumgespielt hat und fiese Tricks angewandt hat, steht man vielleicht irgendwann vor einem Problem.

Daher: Von den Abhängigkeiten lösen und eine Abstraktionsschicht mehr
Ich mach es bei meinen Applikationen immer nach diesem Muster:

image

Das Mapping ist im Grund auch sehr einfach und hab ich mir damals bei Rob Conerys MVC Storefront abgeschaut - der Code kommt auch aus diesem Projekt:

public IQueryable<ProductReview> GetReviews() {


            return from rv in _db.ProductReviews
                   select new ProductReview
                   {
                       ID = rv.ProductReviewID,
                       Author = rv.Author,
                       Body = rv.Body,
                       CreatedOn = rv.ReviewDate,
                       Email = rv.Email,
                       ProductID = rv.ProductID
                   };


        }

Man macht einen ganz normalen Query und macht dann im select Statement sein Mapping auf seine POCOs - das wars :)

Fazit:
Es gibt sicherlich Nachteile bei der Variante, insbesondere wenn eine Persistenzschicht haben möchte und die mit Events um sich wirft sobald irgendwas geändert wurde, aber das juckt mich nicht.
Die Vorteile überwiegen meiner Meinung nach mehr. Man ist völlig flexibel und hat auch die volle Kontrolle, auch wenn man etwas mehr Tippen muss. Aber ich für meinen Teil habe festgestellt, dass Tipparbeit wesentlich unaufwändiger ist als rauszubekommen, warum man diverse Sachen nicht einfach serialisieren kann ;)


Written by Robert Muehsig

Software Developer - from Saxony, Germany - working on primedocs.io. Microsoft MVP & Web Geek.
Other Projects: KnowYourStack.com | ExpensiveMeeting | EinKofferVollerReisen.de