4

Я пытаюсь выяснить, как завершить мою реализацию шаблона репозитория в веб-приложении ASP.NET.Как передать единицу рабочего контейнера в конструктор репозитория с использованием инъекции зависимостей

На данный момент у меня есть интерфейс репозитория на класс домена, определяющий методы, например. загрузки и сохранения экземпляров этого класса.

Каждый интерфейс репозитория реализуется классом, который использует материал NHibernate. Castle Windsor сортирует DI класса в интерфейсе в соответствии с web.config. Пример реализуемого класса приведен ниже:

public class StoredWillRepository : IStoredWillRepository 
    { 
    public StoredWill Load(int id) 
    { 
     StoredWill storedWill; 
     using (ISession session = NHibernateSessionFactory.OpenSession()) 
     { 
     storedWill = session.Load<StoredWill>(id); 
     NHibernateUtil.Initialize(storedWill); 
     } 
     return storedWill; 
    } 

    public void Save(StoredWill storedWill) 
    { 
     using (ISession session = NHibernateSessionFactory.OpenSession()) 
     { 
     using (ITransaction transaction = session.BeginTransaction()) 
     { 
      session.SaveOrUpdate(storedWill); 
      transaction.Commit(); 
     } 
     } 
    } 
    } 

Как указывалось в предыдущем потоке, класс хранилища должен принять единицы работы контейнера (т.е. ISession), а не инстанцировании его в каждом методе.

Я ожидаю, что единый рабочий контейнер будет создан каждой страницей aspx по необходимости (например, в свойстве).

Как указать, чтобы этот экземпляр экземпляра рабочей единицы был передан в конструктор StoredWillRepository, когда Windsor создает его для меня?

Или этот шаблон совершенно неправильный?

Еще раз спасибо за ваш совет.

Дэвид

ответ

0

Технически, ответ на мой вопрос заключается в использовании перегрузки container.Resolve, который позволяет указать аргумент конструктора в качестве анонимного типа:

IUnitOfWork unitOfWork = [Code to get unit of work]; 
_storedWillRepository = container.Resolve<IStoredWillRepository>(new { unitOfWork = unitOfWork }); 

Но давайте посмотрим правде в глаза, ответы предоставлены все другие были более информативными.

+0

Это, как вы говорите, технически ответ на мой вопрос, даже если это не лучшее решение в архитектуре. Я зеленаю тебя. – David

0

У меня есть довольно структуру, подобную вашей, и вот как я решить вопрос:

1) Для того, чтобы задать свой контейнер на каждом методе, у меня есть отдельный класс («SessionManager») который затем я вызываю через статическое свойство. Поступая таким образом, вот пример, используя мой Сохранить реализацию:

private static ISession NHibernateSession 
{ 
    get { return SessionManager.Instance.GetSession(); } 
} 

public T Save(T entity) 
{ 
    using (var transaction = NHibernateSession.BeginTransaction()) 
    { 
     ValidateEntityValues(entity); 
     NHibernateSession.Save(entity); 

     transaction.Commit(); 
    } 

    return entity; 
} 

2) Мой контейнер не создается на каждой странице ASPX. Я создаю экземпляр всей моей NHibernate доброты на странице global.asax.

** Несколько больше вещей возникают **

3) Вам не нужно иметь помощника для создания экземпляра нагрузки. Вы можете также использовать Get вместо Load. Дополнительная информация @Difference between Load and Get.

4) Используя ваш текущий код, вам нужно будет повторить почти одинаковый код для каждого требуемого объекта домена (StoredWillRepository, PersonRepository, CategoryRepository и т. Д.?), Который выглядит как перетаскивание. Вы можете очень хорошо использовать generic class работать над NHibernate, как:

public class Dao<T> : IDao<T> 
{ 
    public T SaveOrUpdate(T entity) 
    { 
     using (var transaction = NHibernateSession.BeginTransaction()) 
     { 
      NHibernateSession.SaveOrUpdate(entity); 
      transaction.Commit(); 
     } 

     return entity; 
    } 
} 

В моей реализации, я мог бы использовать something like:

Service<StoredWill>.Instance.SaveOrUpdate(will); 
+0

Большое спасибо за ваш ввод. Я отвечу на ваши баллы в пронумерованных комментариях ниже: – David

+0

1) Статический ISession? Если я не понял, это означало бы, что ISession (который не является потокобезопасным) разделяется между всеми потоками приложения - очень опасно. Это неправильно? – David

+0

2) Контейнер для чего? Извините, я не понимаю, что вы имеете в виду. – David

1

У меня есть рамки настойчивость, построенный на вершине NHibernate, который используется в нескольких веб-приложениях. Он скрывает реализацию NH за интерфейсом IRepository и IRepository<T>, с конкретными экземплярами, предоставленными Unity (таким образом, я мог бы теоретически заменить NHibernate, например, на Entity Framework довольно легко).

Поскольку Unity не поддерживает (или, по крайней мере, версию, которую я использую), поддерживает передачу параметров конструктора, отличных от тех, которые являются самими инъекциями зависимостей, передача в существующем NH ISession невозможна; но я хочу, чтобы все объекты в UOW делились тем же ISession.

Я решить эту проблему, имея контрольный хранилище класса, который управляет доступом к ISession на каждой нити основы:

public static ISession Session 
    { 
     get 
     { 
      lock (_lockObject) 
      { 
       // if a cached session exists, we'll use it 
       if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY)) 
       { 
        return (ISession)PersistenceFrameworkContext.Current.Items[NHibernateRepository.SESSION_KEY]; 
       } 
       else 
       { 
        // must create a new session - note we're not caching the new session here... that's the job of 
        // BeginUnitOfWork(). 
        return _factory.OpenSession(new NHibernateInterceptor()); 
       } 
      } 
     } 
    } 

В этом примере, PersistenceFrameworkContext.Current.Items получает доступ к IList<object>, который хранится либо ThreadStatic, если не в Веб-контекст или в пределах HttpContext.Current.Items, если он находится в веб-контексте (чтобы избежать проблем с пулом потоков). Первый вызов свойства создает экземпляр ISession из хранимого заводского экземпляра, последующие вызовы просто извлекают его из хранилища. Блокировка немного замедлит работу, но не так сильно, как просто блокировка статического экземпляра ISession с аппроксимацией.

У меня тогда есть BeginUnitOfWork и EndUnitOfWork методы позаботиться о UOW - я специально запретил вложенные UOW, потому что откровенно они были болью для управления.

public void BeginUnitOfWork() 
    { 
     lock (_lockObject) 
     { 
      if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY)) 
       EndUnitOfWork(); 

      ISession session = Session; 
      PersistenceFrameworkContext.Current.Items.Add(SESSION_KEY, session); 
     } 
    } 

    public void EndUnitOfWork() 
    { 
     lock (_lockObject) 
     { 
      if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY)) 
      { 
       ISession session = (ISession)PersistenceFrameworkContext.Current.Items[SESSION_KEY]; 
       PersistenceFrameworkContext.Current.Items.Remove(SESSION_KEY); 
       session.Flush(); 
       session.Dispose(); 
      } 
     } 
    } 

Наконец, пара методов обеспечения доступа к хранилищам домена типа конкретных: (. Здесь PersistentObject<T> является базовым классом, обеспечивающим идентификатор и Равно поддержки)

public IRepository<T> For<T>() 
     where T : PersistentObject<T> 
    { 
     return Container.Resolve<IRepository<T>>(); 
    } 

    public TRepository For<T, TRepository>() 
     where T : PersistentObject<T> 
     where TRepository : IRepository<T> 
    { 
     return Container.Resolve<TRepository>(); 
    } 

Таким образом, доступ к данному хранилищу находится в шаблоне

NHibernateRepository.For<MyDomainType>().Save(); 

Это тогда facaded над таким образом, что вы можете использовать

MyDomainType.Repository.Save(); 

Если данный тип имеет специализированное хранилище (то есть нужно больше, чем он может получить от IRepository<T>), то я создать интерфейс, производный от IRepository<T>, расширяющаяся реализации наследуемых от моего IRepository<T> реализации и в самом типе домена я переопределить статический Repository свойства, используя new

new public static IUserRepository Repository 
    { 
     get 
     { 
      return MyApplication.Repository.For<User, IUserRepository>(); 
     } 
    } 

(MyApplication [который называется что-то меньше Нодди в реальном продукте] класс фасад, который заботится о supplyi нг экземпляра Repository с помощью Unity, так что вы не имеете зависимости от конкретной реализации хранилища NHibernate в классах домена.)

Это дает мне полную pluggability с помощью Unity для реализации хранилища, легкий доступ к хранилищу в коде без прыжков через обручи , и прозрачный, для потоковой обработки ISession.

Существует намного больше кода, чем просто то, что выше (и я упростил код примера), но вы получаете общую идею.

MyApplication.Repository.BeginUnitOfWork(); 
User user = User.Repository.FindByEmail("[email protected]"); 
user.FirstName = "Joe"; // change something 
user.LastName = "Bloggs"; 
// you *can* call User.Repository.Save(user), but you don't need to, because... 
MyApplication.Repository.EndUnitOfWork(); 
// ...causes session flush which saves the changes automatically 

В моем веб-приложение, у меня есть сеанс-за-запроса, так и BeginUnitOfWorkEndUnitOfWork дозвонились в BeginRequest и EndRequest соответственно.

+0

Это потрясающе. Я определенно буду проверять, сможет ли мой код использовать вашу доброту. xD – rebelliard

+0

Для переполнения стека требуется кнопка «Вероятно, правильный ответ, но, честно говоря, я этого не понимаю». Я бы нажал на нее прямо здесь. Я попытаюсь переварить все это в течение выходных, но, возможно, второе место станет пьяным. По крайней мере, я оценил ваш адрес электронной почты! – David

+0

Напиток - отличная идея, которую я буду копировать позже этим вечером :-) Что касается кода, все уровни косвенности и метапрограммирования делают более трудным для чтения. Реальная и полная реализация нашей системы персистентности - это самый сложный код в нашем арсенале здесь, но как только вы это сделали один раз, вам никогда не придется делать это снова. Он многократно оплачивался. –

Смежные вопросы