IDisposable на введенном хранилище - PullRequest
6 голосов
/ 03 апреля 2012

У меня есть следующий репозиторий ADO .Net

public class Repository : IRepository, IDisposable
{
   private readonly IUnitOfWork UnitOfWork;
   private SqlConnection Connection;

   public Repository(IUnitOfWork unitOfWork, connectionString)
   {
      UnitOfWork = unitOfWork;
      Connection = new SqlConnection(connectionString);
      Connection.Open();
   }

   public MyObject FindBy(string userName)
   {
      //...Ado .Net command.ExecuteReader, etc.
   }
}

Этот репозиторий внедряется с контейнером IoC в доменную службу и используется следующим образом:

public class UserDomainService : IUserDomainService
{
   private readonly IRepository Repository;

   public UserDomainService(IRepository repository)
   {
      Repository = repository;
   }

   public User CreateNewUser(User user)
   {
      using(Repository)
      {
         var user = Repository.FindBy(user.UserName);
         if(user != null)
            throw new Exception("User name already exists!");

         Repository.Add(user);
         Repository.Commit();
      }
   }
}

Идея состоит в том, что я всегда помещаю объект Repository в оператор using, поэтому, когда он завершается, соединение закрывается и удаляется, но я вижу это как проблему, поскольку класс Domain Service все еще жив, и если есть второй вызовите его, он потерпит неудачу, так как хранилище уже уничтожено.

Теперь у меня есть полный контроль над всем кодом, и я хочу проектировать только грубые вызовы службы зерна, но есть кое-что во всем этом, что кажется неправильным.

Я делаю это так, чтобы избежать доменной службы, которая знает о методах OpenConnection и CloseConnection в репозитории.

Этот дизайн изначально плох или есть лучший способ сделать это?

После размышления : все дерево зависимостей генерируется на уровне WCF при поступлении запроса, и, конечно, вы можете видеть, что соединение открывается в этот момент, так как это происходит в конструкторе хранилища, поэтому Я считаю, что это не так уж плохо, так как он открыт только на время этого конкретного звонка. Правильно ли я в этом предположении или я делаю что-то ужасно плохое, открывая соединение с БД так рано?

Ответы [ 2 ]

10 голосов
/ 03 апреля 2012

Введите фабрику, которая создает нужные вам экземпляры, а не сам экземпляр.

Возьмите IRepositoryFactory, чтобы вы могли создать IRepository и распоряжаться им каждый раз, когда вы его используете. Таким образом, ни служба домена, ни фабрика не должны быть одноразовыми. Кроме того, и, что важно, вы сохраняете код абстрактным, все еще внедряя реализацию, а не жестко ее кодируя.

public class UserDomainService : IUserDomainService
{
   private readonly IRepositoryFactory RepositoryFactory;

   public UserDomainService(IRepositoryFactory factory)
   {
      RepositoryFactory = factory;
   }

   public User CreateNewUser(User user)
   {
      using (IRepository repository = RepositoryFactory.Create())
      {
         var user = repository.FindBy(user.UserName);
         if(user != null)
            throw new Exception("User name already exists!");

         repository.Add(user);
         repository.Commit();
      }
   }
}

Вам не всегда нужно вводить нужный вам тип. Читая о замке Виндзор (чье мышление - регистрация-разрешение-выпуск), вы обнаружите, что если вы хотите разрешать вещи в неопределенное время в жизни приложения, рекомендуется использовать фабрики типов.

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

1 голос
/ 03 апреля 2012

У вас проблема с владением.Класс UserDomainService не создает IRepository, но он все еще принимает владение этим экземпляром, так как он им располагает.

Общее правило состоит в том, что тот, кто создает объект, должен его разрушить.Другими словами, тот, кто создает объект, является владельцем, и владелец должен уничтожить этот объект.

Есть два решения вашей проблемы.

  1. Создать IRepositoryFactory, как ясно объясняет Адам.Метод CreateNewRepository() на такой фабрике четко сообщает, что вызывающий объект получает право собственности и должен распоряжаться созданным репозиторием.

  2. Пусть тот, кто создает (и внедряет) этот репозиторий, имеет дело сраспоряжение этим хранилищем.Либо вы делаете это вручную в службе WCF, либо используете инфраструктуру IoC / DI.Если вы используете DI-фреймворк, вам, вероятно, следует взглянуть на время жизни каждого веб-запроса или что-то подобное.

Последнее примечание, ваш IRepository реализует IDisposable.При выборе решения 2 вы можете удалить интерфейс IDisposable из IRepository, что скрывает тот факт, что ресурсы задействованы приложением.Спрятать IDisposable от приложения - это хорошо, так как этот интерфейс - утечка абстракции.Вы уже сталкивались с этим, так как вызов Dispose из приложения разрушает все приложение.

...