Base class:
public abstract class Repository : IDisposable
{
private bool _disposed;
private DbContext _context;
public Repository(DbContext context)
{
_context = context;
}
public void SetSomething()
{
//...Access the database and set something for tracing
_context.Database.SqlQuery(....);
}
public void UnSetSomething()
{
//...Access the database and cancel something for tracing
_context.Database.SqlQuery(....);
}
#region Object Disposal
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~Repository()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
// free other managed objects that implement IDisposable only
if (_context != null)
{
_context.Dispose();
_context = null;
}
}
_disposed = true;
}
#endregion
}
Sub Class:
public class ScheduleRepository : Repository
{
private AppContext _context;
public ScheduleRepository(DbContext context)
: base(context)
{
_context = (AppContext)context;
}
#region Object Disposal
bool _disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~ScheduleRepository()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
// free other managed objects that implement IDisposable only
if (_context != null)
{
_context.Dispose();
_context = null;
}
}
_disposed = true;
base.Dispose(disposing);
}
#endregion
}
Logic Class:
public class ScheduleFacade
{
private ScheduleRepository _repository;
public ScheduleFacade()
{
_repository = new ScheduleRepository(AppContext.Create());
}
public ScheduleSetting GetScheduleById(string scheduleId)
{
if (!string.IsNullOrEmpty(scheduleId))
{
_repository.SetSomething();
ScheduleSetting settings = _repository.GetScheduleById(scheduleId);
_repository.UnSetSomething();
return LoadScheduleSettings(settings);
}
else
{
throw new ArgumentException("The scheduleId parameter cannot be empty.");
}
}
private ScheduleSetting LoadScheduleSettings(ScheduleSetting settings)
{
//...code ....
}
}
Is this the correct way to implement IDisposable
on an abstract class implementation? This is not following the DRY principals like it should but I am unclear how to do this properly.
I want to make sure that I am cleaning up my DbContext
appropriately.
EDIT: It appears that more information is needed to clarify what I am doing and why I am passing in DbContext in the constructor (I have added more code above to please re-read). I needed the DbContext in the abstract class to access the database and do some work. Isn't this how I would use an abstract class that is shared among multiple sub classes and thus allowing me to adhere to the DRY principal and centralize future maintenance?
How would I pass the DbContext to the abastract class if I dont pass it through the contstructor (method injection comes to mind but that would require that devs of future repositories might forget to pass the context to the base class).
if you are looking for generic disposable abstract class take a look at the below class , but as @Matthew and @D Stanley said ...the repository should initialize the context each time.
public abstract class DisposableObject : IDisposable
{
private bool _disposed = false;
public virtual bool IsDisposed
{
get { return _disposed; }
}
protected void CheckDisposed()
{
if (IsDisposed)
{
throw new ObjectDisposedException(this.GetType().FullName);
}
}
protected void CheckDisposed(string err)
{
if (IsDisposed)
{
throw new ObjectDisposedException(this.GetType().FullName, err);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (!_disposed)
{
OnDispose(disposing);
}
_disposed = true;
}
protected abstract void OnDispose(bool disposing); // this for the implementor to dispose their items
~DisposableObject()
{
Dispose(false);
}
}
for your implementation of the repository pattern it will be like that:
public abstract class Repository<T> : IDisposable where T : DbContext
{
private bool _disposed;
private T _context;
public Repository(T context)
{
_context = context;
}
protected T Context { get { return _context; } }
public void SetSomething()
{
//...Access the database and set something for tracing
// _context.Database.SqlQuery(....);
}
public void UnSetSomething()
{
//...Access the database and cancel something for tracing
//_context.Database.SqlQuery(....);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (!_disposed)
{
if (_context != null)
{
_context.Dispose();
_context = null;
}
OnDispose(disposing);
}
_disposed = true;
}
// this for the implementor to dispose their items but not the context because it's already disposed from the base class
protected abstract void OnDispose(bool disposing);
~Repository()
{
Dispose(false);
}
}
so in you Schedule Repository do it like that
public class ScheduleRepository : Repository<AppContext>
{
public ScheduleRepository()
:base(new AppContext())
{
}
public Schedule GetById(int id) {
this.Context.Schedules. (....) // blah blah
}
protected override void OnDispose(bool disposing)
{
// if you are working with any thing that you must free it up
// do it here, but not the context
}
}
Edit::::
you logic class will look like that
public class ScheduleFacade
{
private ScheduleRepository _repository;
public ScheduleFacade()
{
_repository = new ScheduleRepository();
}
public ScheduleSetting GetScheduleById(string scheduleId)
{
if (!string.IsNullOrEmpty(scheduleId))
{
_repository.SetSomething();
ScheduleSetting settings = _repository.GetScheduleById(scheduleId);
_repository.UnSetSomething();
return LoadScheduleSettings(settings);
}
else
{
throw new ArgumentException("The scheduleId parameter cannot be empty.");
}
}
private ScheduleSetting LoadScheduleSettings(ScheduleSetting settings)
{
//...code ....
}
}
so it won't depend on EF