Reputation: 229
I'm am trying to learn about the Unit Of Work pattern. However, I am having trouble finding an example that is not too simple or too complicated. So decided to try and write something of my own taking from what I have read. I was wondering if what I have done is acceptable. I am trying to have the layers decoupled. The reasons I created the DatabaseManager was to avoid sending the context to the other layers. In the end this will be the DAL for a MVC application with a middle layer (business)
Thanks for your input.
Code:
public interface IDatabaseFactory : IDisposable
{
ObjectContext Get();
}
public class DatabaseFactory : Disposable, IDatabaseFactory
{
private ObjectContext _dataContext;
#region IDatabaseFactory Members
public ObjectContext Get()
{
return _dataContext ?? (_dataContext = (new MyMemberDatabase()));
}
#endregion
protected override void DisposeCore()
{
if (_dataContext != null)
_dataContext.Dispose();
}
}
public static class DatabaseManager
{
private static readonly Dictionary<Guid, ObjectContext> ContextLists = new Dictionary<Guid, ObjectContext>();
public static ObjectContext GetContext(Guid id)
{
if (!ContextLists.ContainsKey(id))
{
Guid newContextID = id;
ContextLists.Add(newContextID, new DatabaseFactory().Get());
}
return ContextLists[id];
}
public static void RemoveContext(Guid id)
{
if (ContextLists[id] != null)
ContextLists.Remove(id);
}
}
public class Disposable : IDisposable
{
private bool _isDisposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~Disposable()
{
Dispose(false);
}
private void Dispose(bool disposing)
{
if (!_isDisposed && disposing)
{
DisposeCore();
}
_isDisposed = true;
}
protected virtual void DisposeCore()
{
}
}
public interface IUnitOfWork : IDisposable
{
Guid ContextID { get; }
void Commit();
}
public class UnitOfWork : IUnitOfWork
{
private readonly Guid _contextID;
public UnitOfWork()
{
_contextID = new Guid();
_contextID = Guid.NewGuid();
}
#region IUnitOfWork Members
public Guid ContextID
{
get { return _contextID; }
}
public void Commit()
{
DatabaseManager.GetContext(_contextID).SaveChanges();
}
public void Dispose()
{
DatabaseManager.RemoveContext(_contextID);
}
#endregion
}
public abstract class RepositoryBase<T> where T : class
{
private readonly IUnitOfWork _unitOfWork;
private ObjectContext _context;
private IObjectSet<T> _objectSet;
private ObjectContext Context
{
get { return _context ?? (_context = DatabaseManager.GetContext(_unitOfWork.ContextID)); }
}
protected IObjectSet<T> ObjectSet
{
get { return _objectSet ?? (_objectSet = Context.CreateObjectSet<T>()); }
}
protected RepositoryBase(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
}
class MemberRepository : RepositoryBase<Member>
{
public MemberRepository(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
public virtual void Add(Member entity)
{
ObjectSet.AddObject(entity);
}
public virtual void Delete(Member entity)
{
ObjectSet.DeleteObject(entity);
}
public virtual IEnumerable<Member> GetAll()
{
return ObjectSet.AsEnumerable();
}
}
EDIT: New Code
public interface IMemberRepository : IRepository
{
void Add(Member entity);
void Delete(Member entity);
IEnumerable<Member> GetAll();
}
public interface IRepository
{
IUnitOfWork UnitOfWork { get; }
}
public interface IUnitOfWork : IDisposable
{
void Commit();
}
public class DatabaseFactory : Disposable, IDisposable
{
private ObjectContext _dataContext;
public ObjectContext Get()
{
return _dataContext ?? (_dataContext = (new MyMemberDatabase()));
}
protected override void DisposeCore()
{
if (_dataContext != null)
_dataContext.Dispose();
}
}
public static class DatabaseManager
{
private static readonly Dictionary<Guid, ObjectContext> ContextLists = new Dictionary<Guid, ObjectContext>();
public static ObjectContext GetContext(Guid id)
{
if (!ContextLists.ContainsKey(id))
{
Guid newContextID = id;
ContextLists.Add(newContextID, new DatabaseFactory().Get());
}
return ContextLists[id];
}
public static void RemoveContext(Guid id)
{
if (ContextLists[id] != null)
ContextLists.Remove(id);
}
}
public class Disposable : IDisposable
{
private bool _isDisposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~Disposable()
{
Dispose(false);
}
private void Dispose(bool disposing)
{
if (!_isDisposed && disposing)
{
DisposeCore();
}
_isDisposed = true;
}
protected virtual void DisposeCore()
{
}
}
public class UnitOfWork : IUnitOfWork
{
private readonly Guid _contextID;
public UnitOfWork()
{
_contextID = new Guid();
_contextID = Guid.NewGuid();
}
#region IUnitOfWork Members
internal Guid ContextID
{
get { return _contextID; }
}
public void Commit()
{
DatabaseManager.GetContext(_contextID).SaveChanges();
}
public void Dispose()
{
DatabaseManager.RemoveContext(_contextID);
}
#endregion
}
public abstract class RepositoryBase<T> : IRepository where T : class
{
private readonly UnitOfWork _unitOfWork;
private ObjectContext _context;
private IObjectSet<T> _objectSet;
public IUnitOfWork UnitOfWork
{
get { return _unitOfWork; }
}
private ObjectContext Context
{
get { return _context ?? (_context = DatabaseManager.GetContext(_unitOfWork.ContextID)); }
}
protected IObjectSet<T> ObjectSet
{
get { return _objectSet ?? (_objectSet = Context.CreateObjectSet<T>()); }
}
protected RepositoryBase(IUnitOfWork unitOfWork)
{
_unitOfWork = (UnitOfWork)unitOfWork;
}
protected RepositoryBase()
{
_unitOfWork = new UnitOfWork();
}
}
public class MemberRepository : RepositoryBase<Member> ,IMemberRepository
{
public MemberRepository(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
public MemberRepository():base()
{
}
public virtual void Add(Member entity)
{
ObjectSet.AddObject(entity);
}
public virtual void Delete(Member entity)
{
ObjectSet.DeleteObject(entity);
}
public virtual IEnumerable<Member> GetAll()
{
return ObjectSet.AsEnumerable();
}
}
Upvotes: 0
Views: 2085
Reputation: 364259
I think your Unit of Work implementation is good and it should work.
Here you have other possible implementations:
// Interface used by upper layer
public interface IUnitOfWork : IDisposable
{
void Commit();
}
// EF dependent unit of work
public class EFUnitOfWork : IUnitOfWork
{
private readonly ObjectContext _context = new ObjectContext(...);
internal ObjectContext Context
{
get { return _context; }
}
...
}
// EF dependent repository - upper layer uses interface
public abstract class EFRepositoryBase<T> : IRepository<T> where T : class
{
private readonly ObjectContext _context;
private IObjectSet<T> _objectSet;
// yes unit of work and repository is tightly coupled because they both have
// to work with EF
public EFRepositoryBase(EFUnitOfWork unitOfWork)
{
_context = unitOfWork.Context;
}
...
}
If you place unit of work and repository implementations to separate assembly it should be enough. Working with ObjectContext
will be internal implementation of EF DAL assembly and upper layers will have no dependency to EF and its assemblies.
This can bu further improved by initroducing DALFactory. Factory will be responsible for instancing and disposing ObjectContext
and creating unitOfWork and repositories. It will remove tight coupling between UoW and Repository.
I'm also using this implementation which couples UoW and abstract factory for repositories but I have modified it little bit since I wrote that answer. Now my implementation doesn't have property for each repository. Instead it uses generic method which returns repository for requested entity type. Repositories are internally stored in dictionary. But this implementation is not very clean UoW.
Another popular implementation is:
// Interface used by upper layer
public interface IUnitOfWork : IDisposable
{
void SaveChanges();
}
public class UnitOfWork : ObjectContext, IUnitOfWork
{
}
Upvotes: 2