Web服务nHibernate会话工厂问题

Web服务nHibernate会话工厂问题,nhibernate,service,web,sessionfactory,Nhibernate,Service,Web,Sessionfactory,我有一个C#Net网络服务。我正在调用一个dll(C#.Net),它使用nHibernate连接到我的数据库。当我调用dll时,它执行对db的查询并加载父对象“Task”。但是,当dll尝试访问子对象“Task.subtask”时,会抛出以下错误: NHibernate.HibernateException failed to lazily initialize a collection of role: SubTasks no session or session was closed 我

我有一个C#Net网络服务。我正在调用一个dll(C#.Net),它使用nHibernate连接到我的数据库。当我调用dll时,它执行对db的查询并加载父对象“Task”。但是,当dll尝试访问子对象“Task.subtask”时,会抛出以下错误:

NHibernate.HibernateException failed to lazily initialize a collection of role:  SubTasks no session or session was closed
我是nHibernate的新手,所以不确定我遗漏了哪段代码

在调用dll之前,是否需要在web服务中启动工厂会话?如果是,我该怎么做

编辑:添加了web服务代码和CreateContainer()方法代码。此代码在调用dll之前被调用

    [WebMethod]
    public byte[] GetTaskSubtask (string subtaskId)
    {
        var container = CreateContainer(windsorPath);

        IoC.Initialize(container);
        //DLL CALL
        byte[] theDoc = CommonExport.GetSubtaskDocument(subtaskId);

        return theDoc;

    }

/// <summary>
/// Register the IoC container.
/// </summary>
/// <param name="aWindsorConfig">The path to the windsor configuration 
/// file.</param>
/// <returns>An initialized container.</returns>
protected override IWindsorContainer CreateContainer(
   string aWindsorConfig)
{
    //This method is a workaround.  This method should not be overridden.  
    //This method is overridden because the CreateContainer(string) method 
    //in UnitOfWorkApplication instantiates a RhinoContainer instance that 
    //has a dependency on Binsor.  At the time of writing this the Mammoth 
    //application did not have the libraries needed to resolve the Binsor 
    //dependency.

    IWindsorContainer container = new RhinoContainer();

    container.Register(
       Component.For<IUnitOfWorkFactory>().ImplementedBy
          <NHibernateUnitOfWorkFactory>());
    return container;
}
[WebMethod]
公共字节[]GetTaskSubtask(字符串SubTaskKid)
{
var container=CreateContainer(windsorPath);
初始化(容器);
//DLL调用
字节[]theDoc=CommonExport.GetSubtaskDocument(subtaskId);
返回DOC;
}
/// 
///注册IoC容器。
/// 
///温莎配置的路径
///文件。
///初始化的容器。
受保护的覆盖IWindsorContainer CreateContainer(
字符串aWindsorConfig)
{
//此方法是一种变通方法。不应重写此方法。
//此方法被重写,因为CreateContainer(string)方法
//在UnitOfWorkApplication中实例化一个RhinoContainer实例
//对Binsor有依赖性。在撰写本文时,猛犸象
//应用程序没有解析Binsor所需的库
//依赖性。
IWindsorContainer=新RhinoContainer();
集装箱。登记(
Component.For().ImplementedBy
());
返回容器;
}
编辑:正在添加DLL代码和存储库代码

DLL代码

public static byte[] GetSubtaskDocument(string subtaskId)
{
    BOESubtask task = taskRepo.FindBOESubtaskById(Guid.Parse(subtaskId));

    foreach(subtask st in task.Subtasks) <--this is the line that throws the error
    {
    //do some work
    }


}
公共静态字节[]GetSubtaskDocument(字符串subtaskId)
{
BOESubtask task=taskRepo.FindBOESubtaskById(Guid.Parse(subtaskId));

foreach(task.subtask中的subtask st)您显然已经在一个NHibernate数据类中映射了一个集合,并且启用了延迟加载(或者更好:未禁用,因为这是默认行为).NHibernate加载实体并为映射的集合创建代理。一旦访问这些集合,NHibernate就会尝试加载该集合的项。但是,如果在此之前关闭NHibernate会话,则会发生收到的错误。您可能正在通过web服务向web服务公开数据对象e客户端。在序列化过程中,XmlSerializer尝试序列化集合,从而提示NHibernate填充该集合。会话关闭时,会发生错误

防止这种情况的两种方法:

  • 发送响应后关闭会话

  • 禁用集合的延迟加载,以便立即加载它们
在上述编辑之后添加:

在您的存储库中,您可以在using语句中启动UnitsWork。代码完成后,将立即处理这些UnitsWork。我不知道UnitOfWork的实现,但我假设它控制NHibernate会话的生存期。通过处理UnitOfWork,您可能会关闭NHibernate会话。当映射初始化co时llections lazy loaded,这些集合尚未填充且发生错误。NHibernate需要加载实体的会话的确切实例来填充延迟初始化的集合

如果您使用延迟加载,并且有一个在响应完成之前关闭会话的存储库,则会遇到类似的问题。一个选项是在请求开始时初始化UnitOfWork,并在响应完成后关闭它(例如,在Application_BeginRequest中,在Global.asax.cs中的Application_EndRequest中)。这当然意味着将存储库紧密集成到web服务中


在任何情况下,为单个请求创建会话并结合延迟加载都是一个坏主意,将来很可能会产生类似的问题。如果无法更改存储库实现,则可能必须禁用延迟加载。

使用Garland的反馈,我解决了问题。我删除了UnitOfWork将存储库中的代码保存在DLL中,并将对DLL的Web服务调用包装在UnitOfWork中请参见下面的代码mods:

网络服务

[WebMethod]
public byte[] GetSubtaskDocument (string subtaskId)
{
    var container = CreateContainer(windsorString);

    IoC.Initialize(container);

    byte[] theDoc;
    using (UnitOfWork.Start())
    {
        //DLL call
        theDoc = CommonExport.GetSubtaskDocument(subtaskId);
        UnitOfWork.Current.Flush();
    }
    return theDoc;
}
DLL中的存储库调用

public Subtask FindSubtaskById(Guid aSubtaskId)
{ 
    return FindOne(DetachedCriteria.For<Subtask>()
                .Add(Restrictions.Eq("Id", aSubtaskId)));
}
公共子任务FindSubtaskById(Guid AsubTaskKid)
{ 
返回FindOne(DetachedCriteria.For())
.Add(Restrictions.Eq(“Id”,aSubtaskId));
}

感谢您的反馈。但是,我认为我的问题更严重。我没有任何nHibernate会话代码。我使用Windsor打开了一个容器,但没有会话。引发错误的代码在dll中。web服务初始化容器,然后调用dll。dll从数据库中获取对象,并返回一个字节[]到web服务,但它在到达之前会呕吐。但在某个点上,某个地方有人正在打开和关闭NHibernate会话。错误表明NHibernate抱怨某个已关闭的会话,它一定是由某个人打开和关闭的。您使用的是温莎的哪些部分?ActiveRecord,WCF设施,NHibernate Integration?同意……它在某个地方“自动”发生。它是nHibernate集成。hibernate.cfg有一个部分用于。我在上面添加了CreateContainer代码。看看CommonExport.GetSubtaskDocument()中发生了什么会很有趣。我不熟悉NHibernateUnitOfWorkFactory(Rhino.Tools?),也许它有一个可配置的会话管理。但是关于Castle Windsor的一个建议是:您正在为每个请求重新创建IWindsorContainer。您可以在Global.asax.cs中启动应用程序时创建它,因为创建过程可能会非常昂贵。您还可以确保Windsor能够实际跟踪它创建的对象。目前,each请求创建一个新的
[WebMethod]
public byte[] GetSubtaskDocument (string subtaskId)
{
    var container = CreateContainer(windsorString);

    IoC.Initialize(container);

    byte[] theDoc;
    using (UnitOfWork.Start())
    {
        //DLL call
        theDoc = CommonExport.GetSubtaskDocument(subtaskId);
        UnitOfWork.Current.Flush();
    }
    return theDoc;
}
public Subtask FindSubtaskById(Guid aSubtaskId)
{ 
    return FindOne(DetachedCriteria.For<Subtask>()
                .Add(Restrictions.Eq("Id", aSubtaskId)));
}