Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The argument against Entity Framework, and for micro-ORMs #12

Open
pauldotknopf opened this issue Sep 23, 2019 · 7 comments

Comments

@Lux44

This comment has been minimized.

Copy link

commented Sep 23, 2019

Thank you for not advocating building queries from strings :).

Change tracking downside of Entity Framework could have an easy enough workaround: use new DBContext for applying changes, then effects are more obvious.

What doesn't have an easy workaround: startup time of Entity Framework, which is quite noticeable in desktop apps.

Right now is not really a great time to look at EF bug list, or jump into EF, for that matter. The query translator got rewritten, but hasn't really stabilized, as the issue list rightfully indicates. Their test coverage for new/rewritten parts is also not great. "Please try again with nightly builds" has been the standard response for over a month now. Let's hope by the time 3.1 releases in November things have stabilized.

@lufthavn

This comment has been minimized.

Copy link

commented Sep 23, 2019

I stumbled into this article on reddit, and really liked it. I agree with a lot of what you're saying. 😊
However, the quote you've included in the "Change Tracking" paragraph feels like a straw man. I agree that it can be dangerous to throw around change tracking entities to every corner of your system, and thus lose sight of what actually happening in your system, but I feel like most people know this shouldn't be done, and that if you have this problem in your code base, it might be a code smell.
Currently I like to implement a command-handler approach in my system, where a command corresponds to a particular business concern, and has only one handler, where I centralize the preparation and execution of my business logic.
I agree that the change tracking gets in the way when all you need is to load read-only data for display. Not only that but if you're eager loading, and have only a couple of nested collections in your entities, the amount of data EF is querying the database for can get huge. We recently had to debug this problem where I work. A request for ~1000 entities could result in result sets of well over 100,000 lines. Surely this could be optimized in EF, but when I encounter something like this, I like to take a step back and evaluate the tool we're using.
If you're querying read-only data from the database, what exactly do you need an ORM for? I'd advocate for introducing a micro-ORM (Dapper is my current favorite, but I might check out ServiceStack.OrmLite soon 😉). Nobody says you can't use more than one data-access library in your solution.
This approach of using full-fledged ORMs to retrieve and save business entities in your command handlers, and using bare metal ADO.net or a micro-ORM to very efficiently query for read-only data goes very well with the architectural pattern of CQRS, and is why I recommend it a lot to developers who are dealing with large systems and complex domains.

Anyway, the blog post was great, and I generally I agree with you. 😊 Just don't write ORMs off right of the bat, and consider them as a tool as you would any other dependency in your system.
Hope this brain dump makes to sense anybody. 😅

@sharpninja

This comment has been minimized.

Copy link

commented Sep 23, 2019

This blog post is good up to a certain level of application complexity. Most large enterprise projects are going to be dealing with data sets with millions of rows of data, with billions of relational outcomes. Advocating eager loading in such a scenario is non-sense. EF allows you to cherry pick relationships to eager load when using lazy loading by default. If you are using a webserver and constantly eager loading large graphs then you are placing a huge burden on the server to load data that gets dumped in the garbage when the controller returns. If it's a desktop app, you may get away with it for a while, but eventually your app will be holding a couple of gigs of data in RAM and user experience will suffer. On mobile, you should be just as strict with memory usage as on a REST server.

EF was designed to work in all these scenarios and when used correctly, does so admirably. Enterprise code bases need consistency and reliability as their top concerns for maintainability. Designing a system under the assumption that only you or someone of your skill level will be maintaining code is both arrogant and dangerous.

@Grauenwolf

This comment has been minimized.

Copy link

commented Sep 23, 2019

There's other options for ORMs.

For example, Tortuga Chain (which I work on) using database reflection. Rather than just assuming the class exactly matches the table or doing everything using SQL string literals, it compares the table and class definitions at runtime. This dramatically reduces the boilerplate, especially when you don't want every column.

@Grauenwolf

This comment has been minimized.

Copy link

commented Sep 23, 2019

Another is SQL Alchemy which allows you to build complex SQL expressions using an object model. Unfortunately it is Python only at this time.

@Grauenwolf

This comment has been minimized.

Copy link

commented Sep 23, 2019

Regarding boilerplate, consider this line:

Consider this line:

dataSource.Update("dbo.Person", new { ID = personId, Name = "Another Name"}).Exceute();

Why can't all ORMs do this? Why do they usually require manually dealing with connections/contexts and an extra round trip to the database just to perform a simple update?

In my opinion, the only time I should see a using statement in my DB code is when I actually need a transaction. And that should only be needed if I'm updating multiple records.

@mythz

This comment has been minimized.

Copy link

commented Sep 23, 2019

This is the same Update query in OrmLite:

db.UpdateOnly(() => new Person { Id = personId, Name = "Another Name" });

Which if you prefer you could also update from an anonymous object or untyped Dictionary, e.g:

db.Update<Person>(new  { Id = personId, Name = "Another Name" });

db.Update<Person>(new Dictionary<string,object> { 
    ["Id"] = personId, 
    ["Name"] = "Another Name" 
});

Other Update Examples in OrmLite.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.