我知道会话管理的问题在过去已经提出,但我找不到任何可以帮助我解决问题的事情。
我有许多存储库类(例如CustomerRepository,ProductRepository等),我通过Castle Windsor解析(注意:我正在尝试应用三个调用模式here)。我想我最好每个Presenter有一个会话(在我的情况下,这相当于每个表单一个),但是,存储库类需要访问当前活动表单的会话..我不知道我是如何合并这个事实上这些存储库是通过windsor解决的,因为主持人不是单身人士。
例如:
public class SomePresenter
{
private ISomeView view;
private ISession session;
private ICustomerRepository customerRepository;
private IOrderRepository orderRepository;
public SomePresenter(ISomeView view, ISessionFactory sessionFactory, ICustomerRepository customerRepository, IOrderRepository orderRepository)
{
this.view = view;
this.session = sessionFactory.OpenSession();
this.customerRepository = customerRepository;
this.orderRepository = orderRepository;
}
}
存储库需要访问会话...如何使用Windsor进行此操作?我是否被迫通过属性在存储库中手动设置会话,还是有一个我不熟悉的聪明的温莎技巧?
答案 0 :(得分:4)
为什么不将[{1}}注入您的存储库而不是ISession
?
以下是与Autofac一起使用的类似代码,Autofac是一个不同的IoC容器:
ISessionFactory
其中containerBuilder
.Register(c => NHibernateContext.GetSessionFactory().OpenSession())
.As<ISession>()
.InstancePerLifetimeScope();
是我唯一一个配置NHibernate并保留在NHibernateContext
单例上的静态类。
所以我的存储库/查找对象请求会话:
ISessionFactory
然后我的Presenter / View Model / Superivsing Controller / Whatever-The-Heck -oday-Calling-It-This-Month只获取存储库或查找对象:
public MyRepository(ISession session)
{
this.session = session;
}
对于Windsor,我认为(我对它的API非常熟悉,你可能不得不调整它,但它应该给你一个想法)它会像
public MyPresenter(IWhateverRepository repository)
{
// Look ma, the repository has an ISession and I'm none the wiser!
}
也就是说,你告诉容器,“当有人要求ISession时,运行这个获得container.Register(
Component.For<ISession>
.UsingFactoryMethod(
x => x.Resolve<ISessionFactory>().OpenSession())
.LifeStyle.Transient);
的小代理并打开一个会话,然后给他们ISessionFactory
实例。”
但谁关闭ISession
?这取决于您:您可以让存储库在其自己的ISession
方法中明确关闭ISession
。或者你可以依靠你的容器进行关闭和处理;在Autofac中,我使用Dispose()
和ILifetimeScope
执行此操作;在Windsor,我相信你需要查找嵌套容器,这样当你处理一个子容器时,它所创建的所有组件也会被处理掉。
根据我的经验,这通常意味着容器至少泄漏到我的应用程序的“主要形式”:当创建表单时,它会创建一个新的生命周期范围/嵌套容器并显示表单。但是这个级别以下的任何东西都不知道容器;它只是围绕一组组件扔套索,并说“当表格关闭时,摆脱所有这些。”
(这是为了防止在大多数应用程序中使用一个大的鸣笛InstancePerLifetimeScope()
。这在ASP.NET中工作正常,每个请求一个会话,但在Windows窗体中,正如您所说,它是就像陈旧的对象例外的定时炸弹一样。更好地为每个“工作单元”(通常是每个表单或服务)拥有自己的ISession
。)
您也可以设计您的存储库,以便每个方法都需要传入ISession
,但这似乎会变得乏味。
希望能给你一些想法。祝你好运!
答案 1 :(得分:1)
为什么不为每个演示者/控制器提供一个SessionProvider
个人Data Access Objects
(DAO)?您的模型可通过每个Data Access Object
访问。
public sealed class SessionProvider
{
static readonly SessionProvider provider = new SessionProvider();
private static NHibernate.Cfg.Configuration config;
private static ISessionFactory factory;
static ISession session = null;
/// <summary>
/// Initializes the <see cref="SessionProvider"/> class.
/// </summary>
static SessionProvider() { }
/// <summary>
/// Gets the session.
/// </summary>
/// <value>The session.</value>
public static ISession Session
{
get
{
if (factory == null)
{
config = new NHibernate.Cfg.Configuration();
config.Configure();
factory = config.BuildSessionFactory();
}
if (session == null)
{
if (config.Interceptor != null)
session = factory.OpenSession(config.Interceptor);
else
session = factory.OpenSession();
}
return session;
}
}
}
public sealed class OrderDataControl
{
private static ILog log = LogManager.GetLogger(typeof(OrderDataControl));
private static OrderDataControl orderDataControl;
private static object lockOrderDataControl = new object();
/// <summary>
/// Gets the thread-safe instance
/// </summary>
/// <value>The instance.</value>
public static OrderDataControl Instance
{
get
{
lock (lockOrderDataControl)
{
if (orderDataControl == null)
orderDataControl = new OrderDataControl();
}
return orderDataControl;
}
}
/// <summary>
/// Gets the session.
/// </summary>
/// <value>The session.</value>
private ISession Session
{
get
{
return SessionProvider.Session;
}
}
/// <summary>
/// Saves the specified contact.
/// </summary>
/// <param name="contact">The contact.</param>
/// <returns></returns>
public int? Save(OrderItems contact)
{
int? retVal = null;
ITransaction transaction = null;
try
{
transaction = Session.BeginTransaction();
Session.SaveOrUpdate(contact);
if (transaction != null && transaction.IsActive)
transaction.Commit();
else
Session.Flush();
retVal = contact.Id;
}
catch (Exception ex)
{
log.Error(ex);
if (transaction != null && transaction.IsActive)
transaction.Rollback();
throw;
}
return retVal;
}