One of the features that looks insanely cool in many ORMs is Lazy loading of properties. Say you have an "Order" table that stores orders of a customer. Inside your Order business object you have a list of Line Items which is linked to the Products table and you have the customer record, which is linked to the Customer table.
If you want to display a list of Orders, you don’t touch line items, and thus only fetch entries from the Orders table. But in the details page of the order, you fetch the products as well. The cool thing: In your repository, it is only a generic Get
How awesome is this? Lazy loading is amazing, it makes the code so much shorter! Except… it’s a ticking time bomb. If you return a live database object from the repository into your services and views, someone at some point will think "Hey, we should display the order total as well!" and change the code to put in a
Orders.LineItems.Sum(li => li.Total). Well, congratulations, you just introduced a SELECT N+1 into your code!
If you are not careful and just chain lazy collections (e.g., a Product may have a List
I’m no longer using generic repositories with generic Get/GetAll methods. I have a specific OrderRepository that has a method for
IList<order> GetOrderDetails(IEnumerable<int> orderIds) which only populates fields it needs and throws an exception on stuff that doesn’t exist (like Products) or even returns a special type like OrderDetails, and then a GetOrder method that has the full object. Note that I’m returning an IList, not an IQueryable or IEnumerable – by the time it leaves the repository, it is fully populated and disconnected from the database.
Sure, sometimes you end up with a Service method that is literally just a call to the repository, but you still separate business logic into the service layer vs. persistance logic into the repository layer.