Welcher Ansatz verwendet werden soll, hängt von der Verantwortung des Repositorys ab.
Ist das Repository dafür verantwortlich, vollständige Transaktionen auszuführen? dh um Änderungen vorzunehmen und dann Änderungen an der Datenbank zu speichern, indem Sie `SaveChanges? Oder ist es nur ein Teil einer größeren Transaktion und nimmt daher nur Änderungen vor, ohne sie zu speichern?
Fall 1) Das Repository führt vollständige Transaktionen aus (es nimmt Änderungen vor und speichert sie):
In diesem Fall ist der zweite Ansatz besser (der Ansatz Ihres zweiten Codebeispiels).
Ich werde diesen Ansatz nur geringfügig ändern, indem ich eine Fabrik wie diese einführe:
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();
}
}
}
Ich mache diese geringfügige Änderung, um die Abhängigkeitsinjektion zu aktivieren . Dies ermöglicht es uns, später die Art und Weise zu ändern, wie wir den Kontext erstellen.
Ich möchte nicht, dass das Repository die Verantwortung dafür trägt, den Kontext selbst zu erstellen. Die Fabrik, die implementiert IFactory<MyContext>
, hat die Verantwortung, den Kontext zu erstellen.
Beachten Sie, wie das Repository die Lebensdauer des Kontexts verwaltet, den Kontext erstellt, einige Änderungen vornimmt, die Änderungen speichert und dann den Kontext entsorgt. Das Repository hat in diesem Fall eine längere Lebensdauer als der Kontext.
Fall 2) Das Repository ist Teil einer größeren Transaktion (es nimmt einige Änderungen vor, andere Repositorys nehmen andere Änderungen vor und dann wird jemand anderes die Transaktion durch Aufrufen festschreiben SaveChanges
):
In diesem Fall ist der erste Ansatz (den Sie zuerst in Ihrer Frage beschreiben) besser.
Stellen Sie sich vor, Sie werden verstehen, wie das Repository Teil einer größeren Transaktion sein kann:
using(MyContext context = new MyContext ())
{
repository1 = new Repository1(context);
repository1.DoSomething();
repository2 = new Repository2(context);
repository2.DoSomething();
context.SaveChanges();
}
Bitte beachten Sie, dass für jede Transaktion eine neue Instanz des Repositorys verwendet wird. Dies bedeutet, dass die Lebensdauer des Repositorys sehr kurz ist.
Bitte beachten Sie, dass ich das Repository in meinem Code neu einstelle (was eine Verletzung der Abhängigkeitsinjektion darstellt). Ich zeige dies nur als Beispiel. In echtem Code können wir Fabriken verwenden, um dies zu lösen.
Eine Verbesserung, die wir bei diesem Ansatz vornehmen können, besteht darin, den Kontext hinter einer Schnittstelle auszublenden, sodass das Repository keinen Zugriff mehr hat SaveChanges
(siehe das Prinzip der Schnittstellentrennung ).
Sie können so etwas haben:
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);
}
}
Sie können der Schnittstelle weitere Methoden hinzufügen, die Sie benötigen, wenn Sie möchten.
Bitte beachten Sie auch, dass diese Schnittstelle nicht von erbt IDisposable
. Dies bedeutet, dass die Repository
Klasse nicht für die Verwaltung der Lebensdauer des Kontexts verantwortlich ist. Der Kontext hat in diesem Fall eine längere Lebensdauer als das Repository. Jemand anderes wird die Lebensdauer des Kontexts verwalten.
Anmerkungen zum ersten Artikel:
Der erste Artikel schlägt vor, dass Sie nicht den ersten Ansatz verwenden sollten, den Sie in Ihrer Frage beschreiben (fügen Sie den Kontext in das Repository ein).
Der Artikel ist nicht klar, wie das Repository verwendet wird. Wird es als Teil einer einzelnen Transaktion verwendet? Oder umfasst es mehrere Transaktionen?
Ich vermute (ich bin mir nicht sicher), dass in dem Ansatz, den der Artikel beschreibt (negativ), das Repository als langjähriger Dienst verwendet wird, der viele Transaktionen umfasst. In diesem Fall stimme ich dem Artikel zu.
Was ich hier vorschlage, ist anders. Ich schlage vor, dass dieser Ansatz nur in dem Fall verwendet wird, in dem jedes Mal, wenn eine Transaktion benötigt wird, eine neue Instanz des Repositorys erstellt wird.
Anmerkungen zum zweiten Artikel:
Ich denke, dass das, worüber der zweite Artikel spricht, für den Ansatz, den Sie verwenden sollten, irrelevant ist.
Im zweiten Artikel wird diskutiert, ob es auf jeden Fall erforderlich ist, den Kontext zu entsorgen (irrelevant für das Design des Repositorys).
Bitte beachten Sie, dass wir in den beiden Entwurfsansätzen über den Kontext verfügen. Der einzige Unterschied besteht darin, wer für eine solche Entsorgung verantwortlich ist.
Der Artikel sagt, dass die DbContext
Ressourcen zu bereinigen scheinen, ohne dass der Kontext explizit verfügbar gemacht werden muss.