danludwig / tripod

Tripod is an example of one way to architect .NET apps.
34 stars 13 forks source link

Calling SaveChanges from services #1

Closed janhartmann closed 10 years ago

janhartmann commented 10 years ago

I am not currently using your command-structure of querying or writing the database, as my project is not that large.

Instead I am using services, which I call from my controllers to move my business logic away from my UI layer. This service has a dependency on IReadEntities or IWriteEntities. Now my question is, when my controller is dependent on this service, what is the best way of calling .SaveChanges()?

At the moment the .SaveChanges(); is on IWriteEntities (IUnitOfWork), so from my service class I call _entities.SaveChanges(); in my Create/Update IService implementation after _entities.Create(entity);

My concern is that if I update multiple entities I am calling .SaveChanges(); multiple times (from the services) instead of just once from my controller?

Next question is, if my service does both read and write actions, I would only use IWriteEntities? As IReadEntities deattach the entity from the change tracker of EF?

Thanks Dan! ;-)

Jan

danludwig commented 10 years ago

...what is the best way of calling .SaveChanges()?

It depends. For simplicity, I am so far calling it from within command handlers themselves, but one could write decorators that automatically wrap commands in a transaction decorator to handle that automatically, as part of the command handling pipeline.

That said, I know you are not using commands and queries. So the best duplication of this pattern that I can think of without seeing your code is to call it in the service implementation(s). Either way, I prefer not to do this from controllers. Why? Because it is a business concern which units of work get committed together. And business code belongs in a lower layer than the controller.

If you find yourself with multiple service implementations, each of which call SaveChanges, and your code ends up invoking SaveChanges more than once, then you are invoking the services too granularly. You should have each controller action (or at least each controller operation that is a single atomic unit of work) map to only 1 service method. That service method then calls SaveChanges after coordinating all of the operations that go into the transaction.

Controllers should be a thin layer between your users and your business logic. It delegates ALL heaving lifting to the business layer(s), and only acts to translate the business operations into HTTP and UI.

if my service does both read and write actions, I would only use IWriteEntities? As IReadEntities deattach the entity from the change tracker of EF?

Well, IWriteEntities already implements IReadEntities. So if you take a dependency on IWriteEntities, you still have access to the Query and EagerLoad methods on IReadEntities. However if you are reading entities out in order to use them in either Collection properties or Navigation properties on an entity that is going to be created or updated, use the Get method over Query. Why? Because any entities that you are going to be using as Collection or Navigation properties should already be attached to the context. If they are not, EF will try to create them.

On the other hand if you are only querying an entity out of the db to get a scalar property like it's ID, and are not going to be using it as a collection or navigation property during a create or update operation, then .Query will be faster and is safe to use.

janhartmann commented 10 years ago

Thanks for the explanation, makes sense :+1: