I am struggling to figure out the ideal implementation of Entity Framework and the repository pattern. I'm using Entity Framework 4.3 code-first and I just can't seem to wrap my head around good proper usage of entity framework.
I love the things EF brings to the table such as tracked entities, lazy loading, navigation properties, etc. But some of these don't play nice with the repository pattern as I understand it. Let's look at a few examples and maybe you guys can set me straight.
My initial impression of the generic repository is that I don't like it because I don't need the exact same functionality for every entity. For example, I have a repository that stores simple variables (key/value pairs) in the database. I don't need an Add or Delete method because these are static variables. I only need an Update method and a Get method. The generic repository just doesn't seem very robust and doesn't allow for much custom code in the data layer. I also hate when generic repositories return IQueryable<T>
because it gives the upper layers the ability to write expressions directly against the data store, and the upper layers have to assume that the data access technology being used properly implements IQueryable so that it's querying the database and not pulling everything into memory and querying it from there.
It just seems like generic repositories, especially ones that return IQueryable, don't really adhere to good separation of concerns. Maybe you guys can clear that one up for me, but right now I'm using explicitly named repositories and only returning IEnumerable or IList.
I love the concept of navigation properties, but it seems like I rarely get to use them when implementing the repository pattern. For instance, I have a user with a navigation property called "Aliases". If I want to add an Alias for a user it would be super easy to add it via the navigation property.
myUser.Aliases.Add(new Alias { Name="cls", Value="ClearScreen" });
But then where do I call dbContext.SaveChanges()
? I had myUser
passed to me and I used the navigation property to avoid having to inject my IAliasRepository
into the class I'm in. However I now have no way to persist my new alias to the database because my upper layers are unaware of Entity Framework. I now have to inject my IAliasRepository
anyway just so I can all _aliasRepository.SaveChanges()
. Well now that feels like a complete waste. I feel like I should have just used _aliasRepository.AddAlias(newAlias)
instead since I have to have the repository injected anyway.
Self-tracking entities are awesome but they don't lend themselves well to applications where you're trying to hide the data access layer details from the rest of the app. For instance, if I were writing repositories and being totally ignorant that they would be using EF then I would most definitely add an Update(Entity entity)
method. However, in EF you don't need to do that because you can simply make changes to an entity and then call SaveChanges()
. The entity tracks everything that was modified and persists those changes to the database.
var myEntity = _entityRepository.GetEntity("some unique ID"); myEntity.SomeProperty = "new value"; _entityRepository.SaveChanges();
This causes me to eliminate my update methods that I would have included had I not been aware that EF doesn't need them. This makes re-factoring harder down the road because I may have to go back and add proper update methods. My only other option would be to include the methods anyway and then just do nothing with them when I implement my repositories.
public void UpdateEntity(Entity entity) { // Do nothing. EF is tracking changes and they will be persisted when // SaveChanges() is called. }
So my code would look like this, even though it's completely unnecessary.
var myEntity = _entityRepository.GetEntity("some unique ID"); myEntity.SomeProperty = "new value"; _entityRepository.UpdateEntity(myEntity); _entityRepository.SaveChanges();
I suppose having an empty method isn't terrible if I'm just trying to maintain proper separation of concerns for easy refactoring later, but it still feels funny to do that.
Another weird quirk of this pattern is that you have to be extra careful with your DbContext. The same instance of it needs to be injected into all repositories. Otherwise if you pull entities out of one repository and try to associate them with entities from another repository then they won't play nice together because they are from different instances of DbContext. IoC containers make this easier to control, but it's an odd issue for developers just beginning with EF. Not really a problem here so much as just another oddity with Entity Framework and the repository pattern.
What is the proper implementation of the repository pattern with EF? How do you overcome these hurdles?
A navigation property is an optional property on an entity type that allows for navigation from one end of an association to the other end. Unlike other properties, navigation properties do not carry data.
No, the repository/unit-of-work pattern (shortened to Rep/UoW) isn't useful with EF Core. EF Core already implements a Rep/UoW pattern, so layering another Rep/UoW pattern on top of EF Core isn't helpful.
Benefits of Repository PatternIt centralizes data logic or business logic and service logic. It gives a substitution point for the unit tests. Provides a flexible architecture. If you want to modify the data access logic or business access logic, you don't need to change the repository logic.
Navigation properties provide a way to navigate an association between two entity types. Every object can have a navigation property for every relationship in which it participates.
Generic Repository vs Non-generic Repository
Generic repository is not a pattern. Generic repository is just a wrapper. It can be useful for some special scenarios as a base class for specific repositories but in most cases it is just over used and over hyped nonsense.
Navigation properties
Repository itself should be used with aggregate root. The aggregate root is aggregation of multiple related entities where you have repository only for principal because dependencies cannot exist without parent. Repository itself handles loading and persisting all entity types in aggregation.
Even with aggregate roots you will end with some challenges. For example how to handle many-to-many relations? Many to many relation always represent scenario where there is no real principal or dependent entity = they are not in aggregation. If you only set relation between these two entities through navigation property it is still OK but EF also allows you creating related entity through navigation property and it somehow violates repository purpose. You can enforce your repository to test existence of relations but it can cause a lot of additional queries so you will most probably leave it as leaky abstraction of your implementation.
Self-Tracking entities
From your code I think you confused self-tracking entities with attached entities. What you describe is difference between attached and detached entities. If you want to support both scenarios in single code your UpdateEntity
method has a meaning because you must test if entity is attached and attach it + set state if not.
Keeping DbContext in sync
This is where you are missing unit of work. Repository itself wraps only queries and storing entities into context but unit of work handles context creation / retireval and persisting changes to database. As example you can take DbSet
(EF's implementation of repository) and DbContext
(EF's implementation of unit of work).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With