Hacker News new | past | comments | ask | show | jobs | submit login
The many approaches to Entity Framework (jonathanchannon.com)
31 points by jchannon on March 4, 2013 | hide | past | favorite | 15 comments



I have the feeling after having done a couple of big C# and Java projects and spending hours refactoring in the same was as this article does that one of the big problems of statically typed languages is the accumulation of abstraction layers.

Now this might seem an obvious one, since everyone always complains about the complex types Java projects always seem to have, but it goes deeper than 'Java programmers make weird types'.

In my opinion the problem is that interfaces are tightly coupled to types, in the article the author desperately tries to keep IUnitOfWork out of his controllers. He does this by introducing an interface that defines an 'Add' method that itself invokes the 'Add' method on the underlying entity. This seems reasonable but imagine his web application is also a library instead. Someone interfacing with that library would try to keep the authors class abstract from his application layer. Perhaps again defining a class that defines an 'Add' method that hides a call to the 'Add' method the author defined.

The problem lies not so much in that interfaces are not a good way of abstracting, but in the reluctance of developers to use interfaces of external libraries, perhaps because it's impossible to decorate classes with interfaces outside of their definition.

In dynamically typed languages like Ruby, the interface is nothing more than a convention of which methods certain classes should have, and what parameters they can expect. This makes classes in different layers 'magically' compatible with each other and in this case would immediately skip an abstraction layer or two.


The accumulation of abstraction layers is an architectural problem, not a result of statically typed languages. The issue here is the author plainly doesn't understand the unit of work pattern and has made an awful technology choice (EF).

In the case provided, the whole thing was just an architectural mess.

There should be one abstraction which is the transaction and unit of work boundary in the form of a service layer so the application looks like:

controller <--> service <-> model/repository

The transaction and unit of work boundary above is inside the service. This should be entirely controlled by framework/container or chucked in there manually (see example below).

The model/repository should pretty much be whatever the native ORM interface is. it ain't worth abstracting it for shit as you're not going to change it, ever. In the case of NHibernate, which I'd recommend over anything else, your code would look like:

   class UserService {
       // ... DI crud here.
       public BooleanResult AddUser(UserTransferObject userTransferObject) {
           // Validate shit here

           // Create shit
           using (var session = sessionFactory.OpenSession()) // UOW boundary
           using (var trans = session.BeginTransaction()) // Transaction boundary
           {
               var user = User.CreateFromTransferObject(userTransferObject);
               session.Add(user);
               trans.Commit();
               return BooleanResult.Success();
           }
       }
   }

This model can be applied to pretty much everything from API endpoints, web app endpoints, message bus endpoints, queue endpoints, SMTP endpoints. You name it, without having to piss around and let your abstraction leak into the controllers.

You can add a repository abstraction in there as well if you really fancy it but I wouldn't bother.


EF really isn't that bad anymore, the 1.0 times were rough but it's gotten much better.

I agree with your your example though, as well as your point about the model/repository being whatever the native ORM interface is. If you're using EF and you REALLY thought you might swap it out later for something else you could always use its POCO mode so you would at least have your business objects intact and reusable.


Thanks for your comment! Did I have a choice about what ORM I used? No

I agree about your approach regarding a service in a larger app but in a smaller app I have come to the conclusion today that IDBContext injected into a controller would be better than a repo, as you say you arent going to change ORMs in reality and it exposes the native functionality of your ORM.


You may find the accumulation of abstraction layers is often the biggest problem (with maybe DB inefficiencies being a close second). But that's just a factor of complexity: The more "things" you have in a project, the more layers to accomodate these "things" and therefore the greater the complexity and higher the likelyhood of something breaking.

It's not unique to statically typed languages though. Often times when people try to mimic static typing and OOP (badly, a lot of the time) in dynamically typed languages, things are about the same or worse. E.G. PHP


In my humble experience, many abstraction layers in OOP-style projects are the result of the inexperienced developer (acting as an architect) trying to paint himself out of the corner he is most comfortable with, and make everything else look like something he can understand.


I think you're right. I've lost count of how many times I've seen people reimplement PDO from scratch just to mould the whole ensamble to something they find familiar instead of just extending it.

Also, like programminggeek says there's a lot of confusion as to where exactly the logic would go in a MVC project leaving developers to pick... wherever to put it. Of course that leads it to change from project to project and even from programmer to programmer in the same project sometimes.


I agree, but part of it is that the abstraction layers most people are used to just aren't very good. For example, the way people do MVC doesn't leave a good place for logic, so it gets bound to a controller or view or model. None of which is a very good place for logic. So, people add layers, and it gets gross.

Better patters would help avoid all the layers.


How does PHP "mimic" static typing and OOP?


We recently reworked an app developed in mid 2000's and had the delightful job of working through layer upon layer of abstraction and bad OOP (this was when OOP was becoming en-vogue), but a lot of these apps were written in PHP 4, so they mimicked 5.x with hacks... which then were used to implement abstraction.

Yeah, they turned a procedural language into some half-baked-kinda-sorta-looks-like-OOP project in just under 53,000 lines of code.

That kind of "mimic".


I used to go down the repository/unit of work route but I found that just adds loads of unnecessary abstraction and complication. Now I simply expose the data context through an interface and inject that into my controllers. For testing I use a fake data context.


Unfortunately EF doesn't provide a interface. Unlikely I know but what if you swapped your ORM, wouldn't that mean a lot of refactoring?


There's going to be a lot of refactoring either way, whether there are custom abstraction layers in place, and whether there aren't.

The abstraction layers that were originally intended to ease a transition between ORMs or DBs often end up making such a transition far more difficult. They just end up being another huge amount of code that needs some type of refactoring.


ORM swapping is YAGNI.


IDbSet<T> is enough. If you swap your ORM, you're swapping mindset more than just methods, unless you're mapping ORM entities to POCOs in your repository object.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: