Wie implementieren Sie IDisposable Vererbung in einem repository?
Ich bin erstellen eines generischen repository und nicht wissen, was der richtige Weg für die Implementierung der dispose-Funktionen:
Ich bin nicht mit IoC/DI, aber ich werde umgestalten meinen code in der Zukunft, dies zu tun, also:
Mein code:
IUnitOfWork Schnittstelle:
namespace MyApplication.Data.Interfaces
{
public interface IUnitOfWork
{
void Save();
}
}
DatabaseContext Klasse:
namespace MyApplication.Data.Infra
{
public class DatabaseContext : DbContext, IUnitOfWork
{
public DatabaseContext(): base("SQLDatabaseConnectionString")
{
Database.SetInitializer<DatabaseContext>(null);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Same code.
base.OnModelCreating(modelBuilder);
}
#region Entities mapping
public DbSet<User> User { get; set; }
//>>> A lot of tables
#endregion
public void Save()
{
base.SaveChanges();
}
}
}
IGenericRepository Schnittstelle:
namespace MyApplication.Data.Interfaces
{
public interface IGenericRepository<T> where T : class
{
IQueryable<T> GetAll();
IQueryable<T> Get(Expression<Func<T, bool>> predicate);
T Find(params object[] keys);
T GetFirstOrDefault(Expression<Func<T, bool>> predicate);
bool Any(Expression<Func<T, bool>> predicate);
void Insert(T entity);
void Edit(T entity);
void Delete(Expression<Func<T, bool>> predicate);
}
}
GenericRepository Klasse:
namespace MyApplication.Data.Repositories
{
public class GenericRepository<T> : IDisposable, IGenericRepository<T> where T : class
{
private DatabaseContext _context;
private DbSet<T> _entity;
public GenericRepository(IUnitOfWork unitOfWork)
{
if (unitOfWork == null)
throw new ArgumentNullException("unitofwork");
_context = unitOfWork as DatabaseContext;
_entity = _context.Set<T>();
}
public IQueryable<T> GetAll()
{
return _entity;
}
public IQueryable<T> Get(Expression<Func<T, bool>> predicate)
{
return _entity.Where(predicate).AsQueryable();
}
//I delete some of the code to reduce the file size.
#region Dispose
public void Dispose()
{
//HERE IS MY FIRST DOUBT: MY METHOD ITS OK?!
//I saw implementations with GC.Suppress... and dispose in destructor, etc.
_context.Dispose();
}
#endregion
}
}
IUserRepository Schnittstelle:
namespace MyApplication.Data.Interfaces
{
public interface IUserRepository : IGenericRepository<User> { }
}
UserRepository-Klasse:
namespace MyApplication.Data.Repositories
{
public class UserRepository : GenericRepository<User>, IUserRepository, IDisposable
{
public UserRepository(IUnitOfWork unitOfWork) : base(unitOfWork) {}
}
}
UserController controller-Klasse:
namespace MyApplication.Presentation.MVCWeb.Controllers
{
[Authorize]
public class UserController : Controller
{
private IUserRepository _userRepository;
private IProfileRepository _profileRepository;
private IUnitOfWork _unitOfWork;
public UserController()
{
this._unitOfWork = new DatabaseContext();
this._userRepository = new UserRepository(_unitOfWork);
this._profileRepository = new ProfileRepository(_unitOfWork);
}
public ActionResult List()
{
return View(this._userRepository.GetAll().ToList());
}
public ActionResult Create()
{
ViewBag.Profiles = new SelectList(this._profileRepository.GetAll().ToList(), "Id", "Name");
return View(new User());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Exclude = "Id, Status, CompanyId")] User model)
{
ViewBag.Profiles = new SelectList(this._profileRepository.GetAll().ToList(), "Id", "Name");
if (ModelState.IsValid)
{
model.EmpresaId = 1;
model.Status = Status.Active;
_userRepository.Insert(model);
_unitOfWork.Save();
return RedirectToAction("List");
}
else
{
return View();
}
}
}
So, Wann und wie ich meine Entsorgung controller und/oder meine repositories und Kontext?
- Sie sind verwirrend, die Art mit der type-parameter.
Du musst angemeldet sein, um einen Kommentar abzugeben.
AKTUALISIERT ANTWORT:
Beispiel mein controller:
Mit dem code, den Sie jetzt haben, das beste, was zu tun ist, zu überschreiben
Controller.Dispose(bool disposing)
und entsorgen Sie das repository dort.Sobald Sie anfangen, mit einem IOC-container, die alle aus dieser Veräußerung code Weg gehen wird. Bau und Entsorgung passieren sollte in den container-Ebene. Der container wird dann der einzige Ort, wo es bekannt ist oder gepflegt, dass das repository und unit of work sind Einweg.
Aber ich vermute, dass keine dieser Klassen werden müssen, Einweg in den ersten Platz. Sollten Sie verwenden SqlConnection-Objekt in einer using-block. Sie nicht brauchen, um eine Klasse-level-Feld im
DatabaseContext
.Bitte verzeihen Sie die Länge der Antwort. Ich muss es schaffen ein paar Grundlagen, um für meine Empfehlung Sinn zu machen.
S. O. L. I. D.
SOLIDE ... steht für die fünf grundlegenden Prinzipien der objektorientierten Programmierung und design. Die zwei Prinzipien, die von Bedeutung sind hier die
I
undS
.Interface-Segregation-Prinzip (ISP)
Einschließlich
IDisposable
auf dieIGenericRepository<T>
interface explizit gegen den ISP.Er tut dies, weil ein repository ist die Verfügbarkeit (und den Imperativ richtig zu entsorgen, das Objekt) nichts zu, es ist entworfen, den Zweck, die ist zu Erhalten und zu Speichern, aggregate-root-Objekte. Durch die Kombination der Schnittstellen zusammen, erhalten Sie eine nicht-getrennt-Schnittstelle.
Warum ist das wichtig, abgesehen von der Verletzung einige theoretische Prinzip? Ich werde erklären, unten. Aber zuerst muss ich ein anderes cover von der FESTEN Grundsätzen:
Einzelne Verantwortung Prinzip
Habe ich immer diese Artikel, Unter dem Prinzip der Einzigen Verantwortung Ernst, praktisch, wenn ich re-Faktor funktionalen code in gutem OO-code. Dies ist kein einfaches Thema und der Artikel ist sehr dicht. Aber es ist von unschätzbarem Wert, da eine Gründliche Erklärung von SRP.
Verständnis SRP und unter Missachtung der Fehler in 99,9% aller MVC-Controller gibt, die nehmen viele DI Konstruktor-Parameter, der einen Fehler hat, der passend ist hier:
Macht der controller verantwortlich für die beiden, die mithilfe des repository und die Entsorgung der repository überquert, um eine andere Ebene der Abstraktion, und dies verstößt gegen die SRP.
Erklärung:
Du bist also ruft mindestens zwei öffentliche Methoden, die auf das repository-Objekt. Eine zu
Get
ein Objekt und eine zuDispose
des repository. Es ist nichts falsch mit, dass, rechts? Normalerweise Nein, es ist nichts falsch mit dem Aufruf von zwei Methoden für das repository, oder ein beliebiges Objekt.Aber
Dispose()
besonderes ist. Das übereinkommen zur Beseitigung ein Objekt ist, dass es nicht mehr nützlich sein wird, nachdem er entsorgt. Dieses übereinkommen ist ein Grund, warum die mit Muster legt einen eigenen code-block:Dies ist technisch legal:
Aber das gibt eine Warnung vor der Benutzung eines Objekts, nachdem Sie es entsorgt wurde. Dies ist nicht richtig, das ist, warum Sie nicht sehen, Beispiele für diese Verwendung in der MS-Dokumentation.
Wegen dieser einzigartigen übereinkommens
Dispose()
ist mehr eng mit der Konstruktion und Destruktion eines Objekts als auch für die Verwendung von anderen Mitgliedern ein Objekt, auch wenn es ausgesetzt ist als einfache öffentliche Methode.Bau und Entsorgung sind auf dem gleichen niedrigen Niveau der Abstraktion. Da aber der controller ist nicht konstruiert, das repository selbst, er lebt auf einer höheren Ebene der Abstraktion. Bei der Entsorgung des repository, es ist zu erreichen die außerhalb der Ebene der Abstraktion, wenn man mit der repository-Objekt auf einer anderen Ebene. Dies verstößt gegen die SRP.
Code Wirklichkeit
Okay, alle, die Theorie besagt genau das, was, soweit mein code ist betroffen?
Überlegen, was die controller-code aussieht, wenn es über die repository selbst:
Muss der controller erhalten Sie eine nützliche (nicht entsorgt) - repository, wenn es aufgebaut ist. Dies ist eine gemeinsame Erwartung. Aber nicht nenne zwei Methoden in der controller-oder es wird brechen. Dieser controller ist ein one-shot deal nur. Plus, Sie können sogar rufen Sie eine öffentliche Methode innerhalb einer anderen. E. g. die
Update
Methode nennen könnteGet
nach dem speichern des Modells in das repository, um wieder eine aktualisierte Sicht auf Kunden. Dies würde aber sprengen.Abschluss
Erhalt der repository als parameter bedeutet, dass etwas anderes ist verantwortlich für die Erstellung des repository. Dass etwas anderes sollte auch die Verantwortung für die ordnungsgemäße Entsorgung des repository.
Die alternative für die Entsorgung eines Objektes in der gleichen Ebene der Abstraktion, als mit seinen (anderen) öffentlichen Mitglieder, wenn die Lebensdauer des Objekts und der möglichen späteren Nutzung des Objekts ist nicht unter direkter Kontrolle, ist eine tickende Zeitbombe.
Der Regel von
IDisposable
ist diese: Es ist nicht zulässig, ErbenIDisposable
in einer anderen funktionalen interface-Deklaration, weilIDisposable
ist nie ein funktionelles Problem, aber ein detail nur.using (var repo = TypeResolver.Get<IRepository>())
so dass ich don ' T care about SRP oder sonst was, das macht meinen code sauberer.Dispose()
oderDispose(true)
sollten, und Sie nicht legen Sie das repository in einem using-block. Wie können Sie richtig entsorgen Sie das repository, hängt davon ab, wie Sie das erreichen IOC. Verwenden Sie einen container wie Windsor, Unity, oder Autofac? Oder sind Sie über eine factory oder einen service locator? Wenn Sie könnte den code aufruftnew Repository()
, das wird hilfreich sein.DatabaseContext
in seinerDispose
?IDisposable d = _repository as IDisposable;
ist es nicht die gleiche Sache, dass die öffentliche SchnittstelleIGenericRepository<T> : where T : class, IDisposable
???IDisposable
in die Schnittstelle. Wenn Sie machen diese Arbeit mit einem IOC-container,IDisposable
auf der Schnittstelle wird nutzlos sein. Es wird meist nutzlos in Ihren temporären code nun, in der Tat. Es spart Ihnen genau zwei Zeilen von code, den Sie löschen ja eh bald.IDisposable
ist eine Implementierung detailGenericRepository
und auch nicht Teil der funktionalen AbstraktionIGenericRepository<T>
. Ich bin mir nicht sicher, wie Sie Sie äußern einen anderen Weg.Haben Ihre
Interface
Erben vonIDisposable
:Klasse:
IGenericRepository
Objekt ist eine Implementierung detail, und gehört nicht in das interface, sondern auf die Deklaration der Klasse. Das repository-pattern gemeint ist, verstecken die details der Implementierung, wie der Staat eineDbConnection
vom Verbraucher. Durch die SchnittstelleIDisposable
, muss der Verbraucher jetzt nehmen Sie die Verantwortung für die privaten Belange der Klasse.using
- Anweisung auf, ohne zu werfen, um IDisposable. Und es ist nicht eine Frage der "privaten Belange der Klasse", weil eben die Klasse selbst ist Einweg, nicht ein eigenes Mitglied oder so etwas.IDisposable
alle Kosten, einIDisposableGenericRepository<T>
- Schnittstelle konnte verwendet werden.IRepo test
undtest = re repo()
. Mit diesem, wenn ich für den Zugriff auf die dispose-Methode in einem öffentlichen Bereich, muss ich die IDisposable Erben in die Schnittstelle, denn wenn ich die Vererbung in der Klasse, die dispose-Methode wird versteckt für meinen controller.private IRepository _repository;
undthis._repository = new AnyRepository();
stattprivate AnyRepository _repository;
undthis._repository = new AnyRepository();
. So, wenn ich geben Sie den Befehlthis._repository.[intellisense list]
die intellisense listet die Eigenschaften und Methoden der Schnittstelle, anstelle von Eigenschaften und Methoden der Klasse.Einzelne Verantwortung Prinzip
Wenn man sich SRP und verwenden Sie, dass in MVC-es sagt Ihnen, dass, wenn Transaktionen aus mehreren Methoden (
CRUDS
) das ist nicht die richtige Art der Durchführung der Transaktionen.So sollte man diese mehrere (
CRUD
) - Methoden in einer Methode und in diesemTransaction
Methode implementierenusing
für diedatabase context
so können Sie geben, dassdatabase context
für diejenigen, die berufenprivate
Methoden, die die Transaktion Methode. Wenn alle diejenigen (CRUD
) - Methoden erfolgreich ist, können Sie rufen Sie dieCommit
Methode (SaveChanges
). Wenn diese Methoden Versagen Sie nichts tun (=Rollback
). So stellen Sie sicher, dass keineCommit
wird ausgeführt, in denen Methoden, sondern delegieren Sie an die Transaktion Methode.Durch die Umsetzung der 2. Kugel
IDispose
wird nicht benötigt-Repositorys, weilusing
kümmert sich um die Reinigung derdatabase context
.Wenn
Connection Pooling
verwendet wird, das ist eine effiziente Art und Weise der Umsetzung, und es wird skalierbar sein.In der Weise, die Sie setup eine Klasse vor es fühlt sich falsch an. Da im Konstruktor für das Repository Sie können die
database context
ist, und diesecontext
in verschiedene Methoden, aber dann würden Sie brauchen, zu implementierenIDispose
weil Sie brauchen, um Bereinigung der verbindungen selbst. Viele Beispiele für Repositories, deren Umsetzung in diesem Weg aber von einem SRP-Sicht, das ist nicht richtig und sollte geändert werden, nach der 2. Kugel oben.