0

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

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;
}

编辑:添加 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
    }


}

任务存储库

/// <summary>
/// Queries the database for the Subtasks whose ID matches the 
/// passed in ID.
/// </summary>
/// <param name="aTaskId">The ID to find matching Subtasks 
/// for.</param>
/// <returns>The Subtasks whose ID matches the passed in 
/// ID (or null).</returns>
public Task FindTaskById(Guid aTaskId)
{ 
    var task = new Task();
    using (UnitOfWork.Start())
    {
        task =  FindOne(DetachedCriteria.For<Task>()
                    .Add(Restrictions.Eq("Id", aTaskId)));
        UnitOfWork.Current.Flush();
    }
    return task;
}

子任务的存储库

/// <summary>
/// Queries the database for the Subtasks whose ID matches the 
/// passed in ID.
/// </summary>
/// <param name="aBOESubtaskId">The ID to find matching Subtasks 
/// for.</param>
/// <returns>The Subtasks whose ID matches the passed in 
/// ID (or null).</returns>
public Subtask FindBOESubtaskById(Guid aSubtaskId)
{ 
    var subtask = new Subtask();
    using (UnitOfWork.Start())
    {
        subtask =  FindOne(DetachedCriteria.For<Subtask>()
                    .Add(Restrictions.Eq("Id", aSubtaskId)));
        UnitOfWork.Current.Flush();
    }
    return subtask;
}
4

2 回答 2

1

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

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

  • 发送响应后关闭会话

或者

  • 为您的收藏禁用延迟加载,以便立即加载它们

上述修改后的补充:

在您的存储库中,您在 using 语句中启动 UnitsOfWork。一旦代码完成,它们就会被处理掉。我不知道 UnitOfWork 的实现,但我认为它控制 NHibernate 会话的生命周期。通过处理 UnitOfWork,您可能会关闭 NHibernate 会话。当您的映射初始化延迟加载的集合时,这些集合尚未填充并且会发生错误。NHibernate 需要加载实体的会话的确切实例来填充延迟初始化的集合。

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

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

于 2012-04-27T16:59:51.263 回答
0

使用 Garland 的反馈,我解决了这个问题。我从 DLL 的存储库中删除了 UnitOfWork(s) 代码,并将对 DLL 的 Web 服务调用包装在 UnitOfWork 中。请参阅下面的代码模块:

网络服务

[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)));
}
于 2012-04-27T21:10:08.047 回答