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.