Шаблон репозитория в Entity Framework 4, когда мы должны располагать? - PullRequest
31 голосов
/ 28 ноября 2010

Новичок в EF, и я заметил, что использование шаблона репозитория может действительно упростить вещи и позволит мне тоже немного поиздеваться. Так что пока все хорошо.использование objectContext для уничтожения как можно скорее, см. ниже

using (var context = new SchoolEntities())
{    
    context.AddToDepartments(department);    
    context.SaveChanges();
}

Использование шаблона репозитория Я заметил, что на самом деле никто не использует «Использование шаблона», например

using (var repository= new Repository<Student>(new MyContext))
{    
    repository.Add(myStudentEntity)  
    repository.SaveChanges();
}

ЕслиИдея заключается в том, что мы должны избавиться от контекста как можно скорее, иначе память может протечь или стать очень большой?

Кто-нибудь может уточнить?Большое спасибо.

1 Ответ

47 голосов
/ 28 ноября 2010

Да, вы должны располагать контекстом, даже если вы используете репозиторий. Не ясно, какое преимущество дает реализация Repository, потому что вы все еще предоставляете ObjectContext в качестве параметра конструктора, не так ли?

ИМО основной причиной использования репозитория и пользовательского UnitOfWork является постоянное невежество = скрытие кода EF от верхних уровней приложения, поскольку сами ObjectContext + ObjectSet являются реализацией шаблонов репозитория и единицы работы.

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

Для простых и понятных сценариев CRUD я могу обернуть создание и размещение контекста в каждый метод репозитория. В более сложных сценариях я использую дополнительный класс - UnitOfWork (UoW), который оборачивает создание и удаление контекста и инициирует сохранение изменений в базе данных. Он также действует как фабрика для всех репозиториев и передает экземпляр созданного контекста в конструкторы репозиториев.

Большую часть времени я занимаюсь программированием сервисов или веб-приложений, поэтому имею дело с отдельными объектами. Я всегда использую один экземпляр UoW для обработки запросов. Таким образом, UoW создается в начале обработки запроса и освобождается в конце обработки запроса. В случае приложений WinForms / WPF и прикрепленных объектов я думаю, что хорошей идеей является наличие экземпляра UoW / ObjectContext «на форму» - есть статья , описывающая этот подход с помощью сеанса NHibernate (аналогично EF ObjectContext) в MSDN журнал.

Некоторая начальная реализация шаблонов UnitOfWork и Repository:

Держатель контекста и абстрактная фабрика для репозиториев

public interface IUnitOfWork
{
  IRepository<MyEntity> MyEntityRepository { get; }
  // Repositories for other entities

  SaveChanges();
}

Хранилище для отдельных объектов

public interface IRepository<T> where T : class
{
  IQueryable<T> GetQuery();
  void Insert(T entity);
  void Delete(T entity);

  // In very complex scenarios with big object graphs you will probably give up
  // using detached approach and you will always load your entities from DB before
  // deleting or updating them. In such case you will not need Update method at all.

  void Update(T entity);
}

Одноразовая реализация оболочки Enitity UnitOfWork

public class UnitOfWork : IUnitOfWork, IDisposable
{
   private ObjectContext _context = null;

   public UnitOfWork(string connectionString)
   {
     if (String.IsNullOrEmpty(connectionString)) throw new ArgumentNullException("connectionString");
     _context = new ObjectContext(connectionString);
   }

   private IRepository<MyEntity> _myEntityRepository;

   public IRepository<MyEntity> MyEntityRepository
   {
     get
     {
        return _myEntityRepository ?? (_myEntityRepository = new GeneralRepository<MyEntity>(_context));
     }
   }

   public void SaveChanges()
   {
     _context.SaveChanges();
   }

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

   protected virtual void Dispose(bool disposing)
   {
     if (disposing)
     {
       if (_context != null)
       {
         _context.Dispose();
         _context = null;
       }
     }
   }
}

Реализация базового репозитория

public class GeneralRepository<T> : IRepository<T> where T : class
{
  private ObjectSet<T> _set;
  private ObjectContext _context;


  public GeneralRepository(ObjectContext context)
  {
    if (context == null) throw new ArgumentNullException("context");
    _context = context;
    _set = context.CreateObjectSet<T>();
  }

  // Override this method for example if you need Includes
  public virtual IQueryable<T> GetQuery()
  {
    return _set;
  }

  // Override following methods if you are working with object graphs.
  // Methods do not execute operations in database. It is responsibility of 
  // UnitOfWork to trigger the execution

  public virtual void Insert(T entity)
  {
    if (entity == null) throw new ArgumentNullException("entity");
    _set.AddObject(entity);
  }

  // These impelementations are for detached scenarios like web application

  public virtual void Delete(T entity)
  {
    if (entity == null) throw new ArgumentNullException("entity");
    _set.Attach(entity);
    _set.DeleteObject(entity);
  }

  public virtual void Update(T entity)
  {
    if (entity == null) throw new ArgumentNullException("entity");
    _set.Attach(entity);
    _context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
  }
}

Использование при выборе данных

using (var uow = new UnitOfWork(connectionString))
{
  var entity = uow.MyEntitiesRepository.GetQuery().Single(e => e.Id == 1);
  // Do something with entity
}

Использование при изменении данных

using (var uow = new UnitOfWork(connectionString))
{
  uow.MyEntitiesRepository.Update(entity);
  uow.SaveChanges();
}
...