Entity Framework и единица работы

Я использую EF/Repository/Unit of Work, но мне трудно понять некоторые детали. Внутри UnitOfWork я создаю новый EF DbContext (EmmaContext), но заглянув внутрь репозитория, я привожу его, что, как я знаю, неверно, как мне правильно получить контекст внутри репо? Может я вообще на ложном пути?

Вот мой UnitOfWork:

//Interface
public interface IUnitOfWork : IDisposable
{
    void Commit();
}

//Implementation
public class UnitOfWork : IUnitOfWork
{
    #region Fields/Properties
    private bool isDisposed = false;
    public EmmaContext Context { get; set; }
    #endregion

    #region Constructor(s)
    public UnitOfWork()
    {
        this.Context = new EmmaContext();
    }
    #endregion

    #region Methods
    public void Commit()
    {
        this.Context.SaveChanges();
    }

    public void Dispose()
    {
        if (!isDisposed)
            Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        isDisposed = true;
        if (disposing)
        {
            if (this.Context != null)
                this.Context.Dispose();
        }
    }
    #endregion
}

Вот репозиторий:

//Interface
public interface IRepository<TEntity> where TEntity : class
{
    IQueryable<TEntity> Query();
    void Add(TEntity entity);
    void Attach(TEntity entity);
    void Delete(TEntity entity);
    void Save(TEntity entity);
}

//Implementation
public abstract class RepositoryBase<TEntity> : IRepository<TEntity> where TEntity : class
{
    #region Fields/Properties
    protected EmmaContext context;
    protected DbSet<TEntity> dbSet;
    #endregion

    #region Constructor(s)
    public RepositoryBase(IUnitOfWork unitOfWork)
    {
        this.context = ((UnitOfWork)unitOfWork).Context;
        this.dbSet = context.Set<TEntity>();
    }
    #endregion

    #region Methods
    public void Add(TEntity entity)
    {
        dbSet.Add(entity);
    }

    public void Attach(TEntity entity)
    {
        dbSet.Attach(entity);
    }

    public void Delete(TEntity entity)
    {
        dbSet.Remove(entity);
    }

    public IQueryable<TEntity> Query()
    {
        return dbSet.AsQueryable();
    }

    public void Save(TEntity entity)
    {
        Attach(entity);
        context.MarkModified(entity);
    }
    #endregion
}

person Sam    schedule 28.08.2012    source источник


Ответы (4)


Сэм: Я обычно чувствую себя комфортно, когда конкретный репозиторий принимает конкретную единицу работы в ctor:

   public RepositoryBase(UnitOfWork unitOfWork)
   {
        this.context = unitOfWork.Context;
        this.dbSet = context.Set<TEntity>();
   }

Репозиторий и UoW обычно работают согласованно и должны немного знать друг о друге.

Конечно, код, использующий эти классы, знает только об определениях интерфейса, а не о конкретных типах.

person OdeToCode    schedule 29.08.2012
comment
Это в основном то, что я сделал, добавил интерфейс IUnitOfWork, но что вы думаете о модульном тестировании? Вы действительно собираетесь проводить модульное тестирование своих репозиториев и единиц работы? - person Sam; 29.08.2012
comment
Немного подумав, мне стало интересно, как вы абстрагируете реализацию от вызывающего кода и по-прежнему получаете контекст в репо из контейнера IoC? У вас есть образец кода? Спасибо! Кстати, я только что прочитал вашу книгу по MVC 3.0, отличная книга! - person Sam; 29.08.2012
comment
@Sam - контроллер или другой компонент в системе будет работать только с определениями интерфейса, такими как IUnitOfWork. Только ваш контейнер будет знать, что IUnitOfWork сопоставляется с UnitOfWork и обычно настраивается в контейнере, т. е. с помощью StructureMap у вас обычно есть фрагмент кода запуска, который говорит x.For‹IUnitOfWork›().Use‹UnitOfWork(). Что касается тестирования - протестировал бы реальный UoW и репозитории, используя интеграционные тесты вместо модульных тестов. Надеюсь, это имеет смысл. - person OdeToCode; 29.08.2012

Это лучшая статья, которую я читал.

В своем примере они управляют репозиториями следующим образом:

    private SchoolContext context = new SchoolContext();
    private GenericRepository<Department> departmentRepository;
    private GenericRepository<Course> courseRepository;

    public GenericRepository<Department> DepartmentRepository
    {
        get
        {

            if (this.departmentRepository == null)
            {
                this.departmentRepository = new GenericRepository<Department>(context);
            }
            return departmentRepository;
        }
    }

Ваша единица работы содержит контекст, и если ей нужно сослаться на репозиторий, она создает его, если он не был создан, и передает контекст, который он содержит.

В статье также рассказывается, как они преобразовали обычную реализацию контроллера MVC в использование шаблона единицы работы.

person Mark Oreta    schedule 29.08.2012
comment
Отличная статья! Спасибо за ссылку. - person Ben Black; 14.12.2012
comment
Плохая статья! Они создают контекст и репозитории внутри класса unitofwork вместо того, чтобы внедрять их с помощью di Tool. - person Elisabeth; 23.02.2013

В этом сообщении говорится, что вы должны внедрить интерфейс IUnitOfWork в свой база репозитория.

Надеюсь, это поможет. С Уважением

person vfabre    schedule 28.08.2012

Единица работы
Репозиторий

UnitOfWork предназначен для управления атомарными операциями.

Репозиторий инкапсулирует набор объектов, сохраняемых в хранилище данных, и операций, выполняемых над ними.

Если вы передаете контекст или UnitOfWork, то вы не реализуете шаблон UnitOfWork+Repository, что приведет к снятию с UnitOfWork его ответственности. Ака тебе это не нужно.

Правильная реализация, если вы передаете только DbSet. На самом деле большего и не нужно.

person nene    schedule 31.01.2014