Реализация UnitOfWork с помощью Castle.Windsor
Простой вопрос.
Как использовать UnitOfWork с Castle.Windsor, nHibernate и ASP.NET MVC?
Теперь для расширенной информации. В моем стремлении понять шаблон UnitOfWork
у меня с трудом возникает все, что использует прямой пример в сочетании с Castle.Windsor
, особенно в отношении того, как он должен быть установлен.
Вот мое понимание до сих пор.
IUnitOfWork
- Интерфейс
IUnitOfWork
используется для объявления шаблона
-
UnitOfWork
класс должен Commit
и Rollback
транзакции, а Expose a Session
.
Итак, с учетом сказанного, вот мой IUnitOfWork
. (Я использую Fluent nHibernate
)
public interface IUnitOfWork : IDisposable
{
ISession Session { get; private set; }
void Rollback();
void Commit();
}
Итак, вот мой Castle.Windsor
контейнерный загрузочный контейнер (ASP.NET MVC)
public class WindsorContainerFactory
{
private static Castle.Windsor.IWindsorContainer container;
private static readonly object SyncObject = new object();
public static Castle.Windsor.IWindsorContainer Current()
{
if (container == null)
{
lock (SyncObject)
{
if (container == null)
{
container = new Castle.Windsor.WindsorContainer();
container.Install(new Installers.SessionInstaller());
container.Install(new Installers.RepositoryInstaller());
container.Install(new Installers.ProviderInstaller());
container.Install(new Installers.ControllerInstaller());
}
}
}
return container;
}
}
Итак, теперь в моем файле Global.asax
у меня есть следующее...
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
// Register the Windsor Container
ControllerBuilder.Current
.SetControllerFactory(new Containers.WindsorControllerFactory());
}
Repository
Теперь я понимаю, что мне нужно передать ISession
в мой репозиторий. Итак, позвольте мне предположить IMembershipRepository
.
class MembershipRepository : IMembershipRepository
{
private readonly ISession session;
public MembershipRepository(ISession session)
{
this.session = session;
}
public Member RetrieveMember(string email)
{
return session.Query<Member>().SingleOrDefault( i => i.Email == email );
}
}
Итак, я смущен. Используя этот метод, ISession
не будет уничтожен должным образом, а UnitOfWork
никогда не будет использоваться.
Мне сообщили, что UnitOfWork
нужно зайти на уровень веб-запросов, но я не могу найти ничего объясняющего, как это сделать. Я не использую ServiceLocator
любого типа (например, когда я пытался, мне сказали, что это тоже плохая практика...).
Confusion - Как создается UnitOfWork
?
Я просто не понимаю этого, в Генеральная. Я думал, что буду начните передачу UnitOfWork
в Repository
, но если это должен войти в веб-запрос, я не понимая, где эти два относятся.
Дополнительный код
Это дополнительный код для разъяснения, просто потому, что у меня, похоже, есть привычка никогда не предоставлять правильную информацию для моих вопросов.
монтажники
public class ControllerInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
AllTypes.FromThisAssembly()
.BasedOn<IController>()
.Configure(c => c.LifeStyle.Transient));
}
}
public class ProviderInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component
.For<Membership.IFormsAuthenticationProvider>()
.ImplementedBy<Membership.FormsAuthenticationProvider>()
.LifeStyle.Singleton
);
}
}
public class RepositoryInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component
.For<Membership.IMembershipRepository>()
.ImplementedBy<Membership.MembershipRepository>()
.LifeStyle.Transient
);
container.Register(
Component
.For<Characters.ICharacterRepository>()
.ImplementedBy<Characters.CharacterRepository>()
.LifeStyle.Transient
);
}
}
public class SessionInstaller : Castle.MicroKernel.Registration.IWindsorInstaller
{
private static ISessionFactory factory;
private static readonly object SyncObject = new object();
public void Install(Castle.Windsor.IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<ISessionFactory>()
.UsingFactoryMethod(SessionFactoryFactory)
.LifeStyle.Singleton
);
container.Register(
Component.For<ISession>()
.UsingFactoryMethod(c => SessionFactoryFactory().OpenSession())
.LifeStyle.Transient
);
}
private static ISessionFactory SessionFactoryFactory()
{
if (factory == null)
lock (SyncObject)
if (factory == null)
factory = Persistence.SessionFactory.Map(System.Web.Configuration.WebConfigurationManager.ConnectionStrings["Remote"].ConnectionString);
return factory;
}
}
UnitOfWork
Вот мой класс UnitOfWork
дословно.
public class UnitOfWork : IUnitOfWork
{
private readonly ISessionFactory sessionFactory;
private readonly ITransaction transaction;
public UnitOfWork(ISessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
Session = this.sessionFactory.OpenSession();
transaction = Session.BeginTransaction();
}
public ISession Session { get; private set; }
public void Dispose()
{
Session.Close();
Session = null;
}
public void Rollback()
{
if (transaction.IsActive)
transaction.Rollback();
}
public void Commit()
{
if (transaction.IsActive)
transaction.Commit();
}
}
Ответы
Ответ 1
Ваша сессия NH уже является подразделением http://nhforge.org/wikis/patternsandpractices/nhibernate-and-the-unit-of-work-pattern.aspx
Итак, я не уверен, почему вам когда-либо понадобилось бы абстрагировать это дальше. (если кто-нибудь читает этот ответ, знаю, почему я был бы рад услышать, я честно никогда не слышал о какой-либо причине, почему вам нужно...)
Я бы выполнил простой сеанс за запрос. Я не знаю, как вы это сделаете с Виндзором, так как я никогда не использовал его, но с ним довольно просто с StructureMap.
Я завершаю структуру структуры factory для хранения моего сеанса factory и вставляю сессию в репозитории по мере необходимости.
public static class IoC
{
static IoC()
{
ObjectFactory.Initialize(x =>
{
x.UseDefaultStructureMapConfigFile = false;
// NHibernate ISessionFactory
x.ForSingletonOf<ISessionFactory>()
.Use(new SessionFactoryManager().CreateSessionFactory());
// NHibernate ISession
x.For().HybridHttpOrThreadLocalScoped()
.Use(s => s.GetInstance<ISessionFactory>().OpenSession());
x.Scan(s => s.AssembliesFromApplicationBaseDirectory());
});
ObjectFactory.AssertConfigurationIsValid();
}
public static T Resolve<T>()
{
return ObjectFactory.GetInstance<T>();
}
public static void ReleaseAndDisposeAllHttpScopedObjects()
{
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}
}
В файле global.asax на Request_End я вызываю метод ReleaseAndDisposeAllHttpScopedObjects().
protected void Application_EndRequest(object sender, EventArgs e)
{
IoC.ReleaseAndDisposeAllHttpScopedObjects();
}
Итак, сеанс открывается, когда я вызываю свой первый репозиторий, и когда запрос заканчивается, он удаляется. У репозиториев есть конструктор, который принимает ISession и присваивает его свойству. Затем я просто разрешаю репо:
var productRepository = IoC.Resolve<IProductRepository>();
Надеюсь, что это поможет. Есть много других способов сделать это, это то, что работает для меня.
Ответ 2
Является ли проблема несоответствия в лингвистическом/импедансе, что условия библиотеки не преуспевают в том, с кем вы знакомы?
Я новичок в этом [fluent] nhibernate, так что я все еще пытаюсь понять это, но я считаю следующее:
Обычно ассоциируйте ISession с сеансом приложения (например, если это было веб-приложение, вы могли бы подумать о том, чтобы связать создание сеанса с событием Application_Start и убрать, когда приложение отключается - изящно или нет), Когда область приложения исчезнет, то и репозиторий.
UnitOfWork - это всего лишь способ обертывания/абстракции, когда у вас есть несколько действий, которые необходимо выполнить во время обновления, и чтобы они оставались последовательными, они должны быть как последовательно, так и последовательно. Например, при применении более чем тривиальных бизнес-правил для создания, анализа или преобразования данных...
Вот ссылка на сообщение в блоге, в котором приведен пример использования ISession и UnitOfWork в свободном стиле.
http://blog.bobcravens.com/2010/06/the-repository-pattern-with-linq-to-fluent-nhibernate-and-mysql/#comments
EDIT: просто чтобы подчеркнуть, я не думаю, что вы - обязательно используйте единицу работы для каждой операции против репозитория. UnitOfWork действительно нужен, только когда транзакция является единственным разумным выбором, но я тоже начинаю с этого.