简单的问题。
如何将UnitOfWork与Castle.Windsor,nHibernate和ASP.NET MVC一起使用?
现在进行扩展细节。在我理解UnitOfWork
模式的过程中,我很难遇到任何与Castle.Windsor
结合使用直接示例的内容,特别是关于它需要安装的方式。
到目前为止,这是我的理解。
IUnitOfWork
接口用于声明模式UnitOfWork
课程必须Commit
和Rollback
次,并展开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());
}
现在我明白我需要将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
需要进入Web请求级别 - 但我找不到任何解释如何实际解决此问题的方法。我没有使用任何类型的ServiceLocator
(就像我试过的时候,我被告知这也是不好的做法......)。
混淆 - 如何创建
UnitOfWork
?我只是不明白这一点 一般。我的想法是,我愿意 开始将
UnitOfWork
传递给。{Repository
构造函数 - 但如果是的话 必须进入Web请求,我不是 了解两者的关系。
这是额外的澄清代码,因为我似乎习惯于永远不会为我的问题提供正确的信息。
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
课程逐字。
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();
}
}
答案 0 :(得分:5)
您的NH会话已经是一个工作单元http://nhforge.org/wikis/patternsandpractices/nhibernate-and-the-unit-of-work-pattern.aspx
所以我不确定为什么你需要进一步抽象出来。 (如果有人读到这个答案就知道为什么我会很高兴听到,我老实说从来没有听说过为什么你需要......)
我会实现一个简单的每个请求会话。我不知道你怎么会和Windsor一起做这件事,因为我从来没有用过它,但是使用StructureMap相当简单。
我将结构图工厂包装起来以保存会话工厂,并根据需要将会话注入存储库。
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();
}
}
在Request_End上的global.asax文件中,我调用ReleaseAndDisposeAllHttpScopedObjects()方法。
protected void Application_EndRequest(object sender, EventArgs e)
{
IoC.ReleaseAndDisposeAllHttpScopedObjects();
}
因此,当我调用我的第一个存储库时会话被打开,并且当请求结束时它被处理掉了。存储库有一个构造函数,它接受ISession并将其分配给一个属性。然后我就解决了回购:
var productRepository = IoC.Resolve<IProductRepository>();
希望有所帮助。还有很多其他的方法,这对我有用。
答案 1 :(得分:0)
这是一个语言/阻碍不匹配的问题,图书馆术语不会与您熟悉的术语相吻合吗?
我对这个[流利的] nhibernate也是新手,所以我仍然想弄明白,但我的看法是:
通常,将ISession与应用程序会话相关联(例如,如果它是Web应用程序,您可以考虑将会话的创建与Application_Start事件相关联,并在应用程序关闭时进行处理 - 优雅与否) 。当应用程序的范围消失时,存储库也应该消失。
UnitOfWork只是一种包装/抽象事务的方式,在更新期间您需要执行多个操作,并且为了保持一致,它们必须按顺序完成并且每个都成功完成。例如,当将多个简单的业务规则应用于数据创建,分析或转换时......
以下是博客文章的链接,该文章提供了以流畅的方式使用ISession和UnitOfWork的示例。 http://blog.bobcravens.com/2010/06/the-repository-pattern-with-linq-to-fluent-nhibernate-and-mysql/#comments
编辑:只是为了强调,我不认为你 - 必须 - 针对存储库的每个操作使用一个工作单元。只有当交易是唯一合理的选择时才真正需要UnitOfWork,但我也只是从这开始。