Reputation: 491
I plan to create an application having the following layers, and use Entity Framework as my ORM:
I have looked at quite some online tutorials, and articles related to Entity Framework and came across the DbContextScope project which seems like a very nice solution to control a 'business transaction' and ensure all related changes are committed or rolled back. See GitHub: https://github.com/mehdime/DbContextScope
The demo in that GitHub repository contains a scenario where a new entity is created in the database. When I try to map that scenario to my layers, it seems to go like this:
Business: create DTO with property values for the entity to be stored. Create new DbContextScope and call repository in the DAL layer passing the DTO.
DAL: the repository maps the DTO to an entity and add its to the DbContext of Entity Framework.
Business: call the SaveChanges() method on DbContextScope which in its turn calls the SaveChanges() on the DbContext of Entity Framework.
In the demo the ID of the entity being stored is already known when the DTO is created. However I am looking for a way to determine the ID automatically assigned by EF once the SaveChanges() method on the DbContextScope is called in the business layer. Since I am in the business layer at this point, I do not have access to the entity anymore, hence I cannot reach the ID property of that entity anymore.
I guess I can only determine the ID by querying the database for the record just created, but this is only possible if the original DTO contains some unique identifier I could use to query the database. But what if I do not have a unique value in my DTO I can use to query?
Any suggestions on how to solve this, or do you recommend an alternative approach to my layers? (e.g. use entities in business layer as well - despite this sounds like the wrong thing to do)
Upvotes: 1
Views: 283
Reputation: 34908
I use Mehdime's context scope wherever I can, as I've found it to be an exceptional implementation of a unit of work. I agree with Camilo's comment about the unnecessary separation. If EF is trusted to serve as your DAL then it should be trusted to work as designed so that you can leverage it completely.
In my case, my controllers manage the DbContextScope and I utilize a repository pattern in combination with a DDD design for my entities. The repository serves as the gate keeper for the interactions with the context scoped and located with the DbContextLocator. When it comes to creating entities, the repository serves as the factory with a "Create{X}" method where {X} represents the entity. This ensures that all required information needed to create the entity is provided, and the entity is associated with the DbContext before being returned so that the entity is guaranteed to always be in a valid state. This means that ones the context scope SaveChanges call is made, the bounding service has the entity with it's assigned ID automatically. ViewModels / DTOs are what the controller returns to the consumer. You also do have the option to call the DbContext's SaveChanges within the boundary of the DbContextScope which will also reveal IDs prior to the context scope SaveChanges. This is more of a very edge-case scenario for when you want to fetch an ID for loosely coupled entities. (No FK/mapped relationship) The repository also services "Delete" code to ensure all related entities, rules, and such are managed. While editing entities falls under DDD methods on the entity itself.
There may be a more purist argument that this "leaks" details of the domain or EF specific concerns into the controller, but my personal opinion is that the benefits of "trusting" entities and EF within the scope of the bounded context within the service layer far, far, outweighs anything else. It's simpler, and allows you a lot of flexibility in your code without the need for near-duplicate methods propagating to supply consumers with filtered data, or complex filtering logic to "hide" EF from the service layer. The basic rule I follow is entities are never returned outside of the boundary of their context scope. (No detach/reattach, just Select into ViewModels, and managing Create/Update/Delete on entities based on passed in view models/parameters.)
If there are more specific concerns / examples you can provide, please feel free to add some code outlining where you see those issues.
Upvotes: 1