c# entity framework: correct use of DBContext class inside your repository class
Solution 1:
I think you should not follow the first article, and I will tell you why.
Following the second approach you're loosing pretty much every feature that Entity Framework provides via the DbContext
, including its 1st-level cache, its identity map, its unit-of-work, and its change tracking and lazy-loading abilities. That's because in the scenario above, a new DbContext
instance is created for every database query and disposed immediately afterwards, hence preventing the DbContext
instance from being able to track the state of your data objects across the entire business transaction.
Having a DbContext
as a private property in your repository class also has its problems. I believe the better approach is having a CustomDbContextScope. This approach is very well explained by this guy: Mehdi El Gueddari
This article http://mehdi.me/ambient-dbcontext-in-ef6/ one of the best articles about EntityFramework I've seen. You should read it entirely, and I believe it will answer all your questions.
Solution 2:
Let's assume, you have more than one repository and you need to update 2 records from different repositories. And you need to do it transactional (if one fails - both updates rollbacks):
var repositoryA = GetRepository<ClassA>();
var repositoryB = GetRepository<ClassB>();
repository.Update(entityA);
repository.Update(entityB);
So, if you have own DbContext for each repository (case 2), you need to use TransactionScope to achieve this.
Better way - have one shared DbContext for one operation (for one call, for one unit of work). So, DbContext can manage transaction. EF is quite for that. You can create only one DbContext, make all changes in many repositories, call SaveChanges once, dispose it after all operations and work is done.
Here is example of UnitOfWork pattern implementation.
Your second way can be good for read-only operations.
Solution 3:
The root rule is : your DbContext lifetime should be limited to the transaction you are running.
Here, "transaction" may refer to a read-only query or a write query. And as you may already know, a transaction should be as short as possible.
That said, I would say that you should favor the "using" way in most cases and not use a private member.
The only one case I can see to use a private member is for a CQRS pattern (CQRS : A Cross Examination Of How It Works).
By the way, the Diego Vega response in the Jon Gallant's post also gives some wise advice :
There are two main reasons our sample code tends to always use “using” or dispose the context in some other way:
The default automatic open/close behavior is relatively easy to override: you can assume control of when the connection is opened and closed by manually opening the connection. Once you start doing this in some part of your code, then forgetting to dipose the context becomes harmful, because you might be leaking open connections.
DbContext implements IDiposable following the recommended pattern, which includes exposing a virtual protected Dispose method that derived types can override if for example the need to aggregate other unmanaged resources into the lifetime of the context.
HTH
Solution 4:
Which approach to use depends on the responsibility of the repository.
Is the responsibility of the repository to run full transactions? i.e. to make changes and then save changes to the database by calling `SaveChanges? Or is it only a part of a bigger transaction and therefore it will only make changes without saving them?
Case #1) The repository will run full transactions (it will make changes and save them):
In this case, the second approach is better (the approach of your second code sample).
I will only modify this approach slightly by introducing a factory like this:
public interface IFactory<T>
{
T Create();
}
public class Repository : IRepository
{
private IFactory<MyContext> m_Factory;
public Repository(IFactory<MyContext> factory)
{
m_Factory = factory;
}
public void AddCustomer(Customer customer)
{
using (var context = m_Factory.Create())
{
context.Customers.Add(customer);
context.SaveChanges();
}
}
}
I am doing this slight change to enable Dependency Injection. This enables us to later change the way we create the context.
I don't want the repository to have the responsibility of creating the context it self. The factory which implements IFactory<MyContext>
will have the responsibility of creating the context.
Notice how the repository is managing the lifetime of the context, it creates the context, does some changes, save the changes, and then disposes of the context. The repository has a longer lifetime than the context in this case.
Case #2) The repository is part of a bigger transaction (it will do some changes, other repositories will make other changes, and then someone else is going to commit the transaction by invoking SaveChanges
):
In this case, the first approach (that you describe first in your question) is better.
Imagine this going on to understand how the repository can be a part of a bigger transaction:
using(MyContext context = new MyContext ())
{
repository1 = new Repository1(context);
repository1.DoSomething(); //Modify something without saving changes
repository2 = new Repository2(context);
repository2.DoSomething(); //Modify something without saving changes
context.SaveChanges();
}
Please note that a new instance of the repository is used for each transaction. This means that the lifetime of the repository is very short.
Please note that I am new-ing up the repository in my code (which is a violation of dependency injection). I am just showing this as an example. In real code, we can use factories to solve this.
Now, one enhancement that we can do to this approach is to hide the context behind an interface so that the repository no longer has access to SaveChanges
(take a look at the Interface Segregation Principle).
You can have something like this:
public interface IDatabaseContext
{
IDbSet<Customer> Customers { get; }
}
public class MyContext : DbContext, IDatabaseContext
{
public IDbSet<Customer> Customers { get; set; }
}
public class Repository : IRepository
{
private IDatabaseContext m_Context;
public Repository(IDatabaseContext context)
{
m_Context = context;
}
public void AddCustomer(Customer customer)
{
m_Context.Customers.Add(customer);
}
}
You can add other methods that you need to the interface if you want.
Please note also that this interface does not inherit from IDisposable
. Which means that the Repository
class is not responsible for managing the lifetime of the context. The context in this case has a larger lifetime than the repository. Someone else will be managing the lifetime of the context.
Notes on the first article:
The first article is suggesting that you shouldn't use the first approach you describe in your question (inject the context into the repository).
The article is not clear on how the repository is used. Is it used as a part of a single transaction? Or does it span multiple transactions?
I am guessing (I am not sure), that in the approach that the article is describing (negatively), the repository is used as a long-running service that will span a lot of transactions. In this case I agree with the article.
But what I am suggesting here is different, I am suggesting that this approach is only used in the case where the a new instance of the repository is created every time a transaction is needed.
Notes on the second article:
I think that what the second article is talking about is irrelevant to which approach you should use.
The second article is discussing whether it is necessary to dispose of the context in any case (irrelevant to the design of the repository).
Please note that in the two design approaches, we are disposing of the context. The only difference is who is responsible for such disposal.
The article is saying that the DbContext
seem to clean up resources without the need of disposing the context explicitly.