Originally published @ hasnode.dev.
Preface
I have been a backend developer for easily 14 years, all of them in .Net, whatever version was on at the time. During the better part of those 14 years I have gone through all the so-called goodies Entity Framework provides, from related properties and lazy loading to data model change tracking, database-first and code-first. Care to guess how many times I felt satisfied about how having all that at my disposal cut my go-to-production time? Zero.
I guess that at this point it is fair to set some perspective: I have always worked for mutinational enterprises, such as Hewlett-Packard, Procter & Gamble, IBM, etc. and have had similar customers, including AT&T briefly. The projects these companies usually need are not standard. Not in at least 50% of their requirements. Back 14 years ago, I used to design and code entire systems for P&G from waterfall documents, meaning complete project, from start to finish, all in one go, no sprints, no nothing. Zero agile, all waterfall. Not once in those projects, listen carefully, NOT ONCE did I finish a single project where the so-called features of Entity Framework did not trip me somehow.
Once lazy loading did not work for me and I was terrified because I was pressed for time. The problem was that the SQL connection object was being disposed before I was needing the data. Bottomline: Lazy loading to the trash.
Database-first approach saves you time with models, you say. Yes, and also create big weird, not-easily maintainable classes where I can do little to no changes or tweaks. So code first? Don't make me laugh. I have always needed non-standard, out-of-the-beginners-box T-SQL for these big enterprises' databases. So bye bye you too, ultimately-useless feature.
And I have stories for most of the mayor "great features" of Entity Framework. Sure, I was young and naive, and having skipped Computer Science and learned all I know by myself (because I originally studied Chemical Engineering), I did not fully understand where the problem relied. I could not pinpoint why I continued to have the issues I was having.
My Major Discovery: RAD Tools
There was this thing I had heard of from time to time, RAD Tools. RAD stands for Rapid Application Development, and basically they exist for the lesser developers. If you are one of those, I mean no disrespect, and I say this with you in my heart: STOP USING RAD TOOLS. Most of them are there to lure you towards something else.
What do I mean? I will explain right now in the context of Entity Framework. Microsoft and many other RAD-tool providers use RAD tools to lure you towards their paid products or to gain you on their side somehow. EF is in itself a RAD-tool when you use automatic modeling creation with the database-first approach, as example here.
Another data-related example:
"Look!", they say, "a SqlObjectDataSource component for your Windows Forms data-bound controls. Just drop it on your form, make the connection string, select a Table, pick the ID and the text columns and voilá!, you have now filled your combo box!"
First project I tried it? By the 4th control in need of data it could not fulfill its obligation.
Long story short: I have always had to read A LOT of hacks and how-to tweaks to get around problems that aren't really that big of a problem, just to continue to use a RAD tool. With EF (and other RAD tools) I had to learn a lot to make things work. The cure was always more painful than the illness.
So I then stopped using EF altogether and started using pure ADO.net, since no other ORM's really felt adequate for one reason or another at the time. Guys, trust me when I say this: I felt free! I could do any custom T-SQL, run complex and simple queries alike, connect controls and fetch data any way and at any moment I needed, could update the database reliably and with full control of the data, steps, order of steps and transactions. It was magical.
When I did this I was finally able to actually learn how to program in layers. If you don't see the relation, read this next sub-section.
So RAD Tools? Was That It?
Ok, read this section if you want to know why RAD tools do not let you create truly scalable and modular software.
The story of RAD tools is: They cross layer boundaries for "your convenience", or so they say. EF, for instance, provides the engine for data layers, but you cannot really unit test your work because you're always dependant on a database, and also creates the models for you in database-first, so you are crossing the repository layer by letting EF dictate your models and how they are coded. Code-first is far less intrusive, of course.
The main RAD feature of EF that will always cross at least 1 boundary is its querying system, LINQ-to-Entities. You are encouraged to write LINQ outside the repository boundary, automatically imposing knowledge of the underlying data structure that should have never crossed the boundary in the first place. LINQ used like this will never let you have a properly layered system, and this is exactly how Microsoft tells you, and how teachers teach this at colleges. Unlearn all this.
Let's quickly jump to ASP.Net controllers: RAD tool there is the ModelState.IsValid()
you see in a lot of controller samples out there, including Microsoft's MSDN. Guess what? Layer crossing. The sole responsibility of a controller is to translate data from and to the HTTP protocol standard. That's it. It has no business running data validation. None. But it's handy, right?
So that's the story: If you are a lesser programmer, RAD tools out there have a lot of appeal, but you'll suffer for sure when your team lead or the customer comes with some fancy requirement whose proper implementation requires code in places the RAD tools you used had hidden, again, for your so-called convenience. You are left with a project that is closed in places where you need it open.
How Do I Choose, Then?
Simple, first learn proper programming. That includes SOLID and layered programming as a minimum. Once you have acquired this knowledge, you'll have a good set of concepts that will help you jugde any library or framework you are offered. Not all RAD tools are non-sense, and nowadays is not like my "back then". Most frameworks and tools in this day and age are being developed with the O of SOLID in mind, so this is less and less a problem as time goes by.
Enough Digressing: The Technical Why's
In order to achieve scalable and maintainable software, it needs to follow SOLID and layered programming. If you are careful with this, most problems will find their solution rather quickly.
As for the topic at hand, yes, you can use EF in a well-architectured system. My recommendation is to turn off model tracking and use code-first and don't use the migrations feature. There are better ways to do this, especially if we are talking microservices here. But even like this I still dislike it. Why, you say?
Entity Framework Is an Implementation of the Unit Of Work Pattern
This is a good thing, but only for non-web API projects. Unit Of Work is good for Windows Forms, WPF or in general, desktop applications. It has no use in RESTful systems.
In REST, you follow a strict CRUD pattern, and more importantly, you usually only work with one entity (or model) at a time. If you read about the Unit Of Work pattern, you'll discover that it is useful when handling multiple models. It produces no benefits in single-entity scenarios. Since entity tracking is turned on by default in EF, the first time I got an error telling me that "there's another object being tracked for this entity ID", I almost lost my mind in rage.
In RESTful web API's, you are constantly serializing and de-serializing objects, so you don't actually care about tracking entity property changes around, which is something actually very useful in Windows Forms and WPF applications when binding data to entity collections, which I highly recommend. In RESTful web API's we just don't have an object being exposed to a UI where changes can be accumulated on the object to then save them in the repository (database). This is not a web API thing. Unit Of Work is meaningless here.
So here you are, paying in performance for a bunch of EF features that you don't need and some actually interfere with what you want to achieve. And this, ladies and gentlemen, is probably the main reason I suffered during my early years using EF for enterprise-grade web projects. I just didn't have the knowledge to understand it back then. I hope that you, avid reader, find this information useful so you don't have to continue to suffer in your journey. Make your journey better than mine.
So, Go ORM-less?
Not at all. For RESTful web API's, I highly recommend Dapper. Fastest ORM in the world for .Net, beaten only by a pure ADO.net approach, and not even by much. Dapper has everything your need and more to create a very well layered software system with a clear separation of concerns everywhere that also provides cool things like multiple result sets or the destructuring of joined queries into hierarchical object trees.
So No EF in REST At All?
There's one exception I know of where you can use EF because it pretty much is the only option: IQueryable<T>
extension libraries like **Microsoft.AspNetCore.OData. This is very useful and handy, and follows a known standard (OData) but requires an implementation of the IQueryable<T>
interface, and that is one thing I don't ever want to code (said implementation).
So until the day someone does an IQueryable<T>
implementation for Dapper, feel free to have the 2 ORM's in your RESTful project.
DbUp: A Replacement for Database Migrations
There's a lot to talk about when it comes to DbUp, but the most important aspect is that upgrade steps are not constrained to running SQL code. They are actually defined by an interface, and therefore you can make anything that .Net can do as a database upgrade step.
In microservices, this is amazingly helpful: Data is communicated through asynchronous channels such as queues (RabbitMQ, Kafka, ActiveMQ, etc.). If part of your database upgrade includes data modification, you can create an upgrade step that actually broadcasts the data changes in bulk using a DbUp step.
Final Thoughts
There's more fabric to cut here. In microservices, being able to create a redistributable nuget package with your public models is life-saving: One project for all microservices, and why not?, for your downstream consumers too. This is yet one more reason to despise database-first.
Yes, all in all, you can get away with a well-structured RESTful system using EF, but to do this you will have suffered unnecessarily in the process.
Ok, I think I said most of what angers me about Entity Framework. Do you disagree? No problem. We can have a healthy discussion about it.
Happy coding you all.
Top comments (12)
I do agree up to an extent as some of what you have mentioned reflect in my real world projects. I am ADO and ADO.NET person and I started developing software products from 2002 onwards, since 2016 I have been using Dapper and EF in my projects. I designed a project template which use both ORMs but implimented seprate pipe lines using clean architecture and now every time I have a new build it only take me 5 minutes to spin a new API and get my team to start developing.
Exactly, there are so much better ways to go than EF nowadays, right? It is just a matter of putting some thought on your design and add on your past experiences.
Personally I work with the repository pattern where I enclose Dapper whenever the customer and the project allows (some enterprises are married to EF, unfortunately). Then, with super-dumb repositories I do unit testing from the services layer up and free myself from the oddities of unit testing code that depends on a database.
Hi @webjose I am on the same page as you. I normally have two projects Libs one for Dapper Repo and EF Repo and both are dum repositories. Basically, Controller->ServiceLayer->EF or Dapper and if I have two or more DBs which might produce one DTO from 10 different tables, I prefer to use Dapper. In the past, I had the opportunity to work on a very large project where multiple Dev teams were working on different bits of the project and we had a lot of redundant code, so I recommended Generic Repos and immediately I got a lot of push back from Tech leads about it being not very efficient, but in short it made life for every one lot easier.
Agreed. I work with "model features", where each feature is represented by an interface. By doing this, I can write generic repository base classes based on the interface, and then simply inherit from the correct repository according to the features boasted by the model. It is super efficient. Exactly the opposite your tech leads told you.
Cheers.
Hi @webjose I have read about Model Features and done some practice code but will love to see a real life or indepth example. Any blog you can recommend?
Thanks.
Hi. No recommendations. I have never seen anybody doing it. I think I am the only one that does it, and because I have never blogged about this, I don't think you'll find anything. I will try to write something up within 30 days.
If you can find time if not still big thank you it was good to share thoughts.
Hello @Manik, I finally wrote about Model Features.
Happy reading.
Hi @webjose read your article. I really appreciate the time you took to explain the concept with really good example code. My Generic Repository is about 90 percent as you explained, but upwards in the service layer and other parts of the project, I was not using the Model features concept. I have started work on a template project to see how it works across all layers.
Good luck! Glad you liked it.
Disagree about Unit of work is meeningless in API. Any where you develop always need to deal with multiple model in the same time.
Hello. It is true that you deal with more models. It is also true that you can do this without the Unit of Work pattern, especially if you have one implementation that is so intrusive.