Попытка реализовать UnitOfWork в Entity Framework - PullRequest
0 голосов
/ 28 февраля 2011

Я пытаюсь узнать о модели единиц работы. Однако мне трудно найти пример, который не слишком прост или слишком сложен. Поэтому решил попробовать написать что-то свое, исходя из прочитанного. Мне было интересно, если то, что я сделал, приемлемо. Я пытаюсь отделить слои. Причины, по которым я создал DatabaseManager, заключались в том, чтобы не отправлять контекст другим слоям. В конце это будет DAL для приложения MVC со средним уровнем (бизнес)

Спасибо за ваш вклад.

Код:

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();
        }

    }

РЕДАКТИРОВАТЬ: новый код

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();
    }
}

1 Ответ

2 голосов
/ 28 февраля 2011

Я думаю, что ваша реализация Unit of Work хороша, и она должна работать.

Здесь у вас есть другие возможные реализации:

// 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;
    }

    ...
}

Если вы помещаете единицы работы и реализации репозитория в отдельную сборку, этого должно быть достаточно.Работа с ObjectContext будет внутренней реализацией сборки EF DAL, а верхние уровни не будут зависеть от EF и его сборок.

Это может быть улучшено путем введения DALFactory.Фабрика будет отвечать за создание и удаление ObjectContext и создание unitOfWork и репозиториев.Это устранит тесную связь между UoW и репозиторием.

Я также использую эту реализацию, которая связывает UoW и абстрактную фабрику для репозиториев, но я немного изменил ее, так как написал этот ответ.Теперь моя реализация не имеет свойства для каждого хранилища.Вместо этого он использует универсальный метод, который возвращает хранилище для запрошенного типа объекта.Репозитории хранятся внутри словаря.Но эта реализация не очень чистая UoW.

Другая популярная реализация:

// Interface used by upper layer
public interface IUnitOfWork : IDisposable
{
    void SaveChanges();
}

public class UnitOfWork : ObjectContext, IUnitOfWork
{

}
...