Web服务nHibernate SessionFactory问题

时间:2012-04-27 16:43:17

标签: nhibernate service web sessionfactory

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

NHibernate.HibernateException failed to lazily initialize a collection of role:  SubTasks no session or session was closed

我是nHibernate的新手,所以不确定我缺少哪些代码。

在调用dll之前,是否需要在我的Web服务中启动Factory会话?如果是这样,我该怎么做?

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

2 个答案:

答案 0 :(得分:1)

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

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

  • 在发送回复后关闭会话

  • 禁用系列的延迟加载,以便立即加载

在上述编辑后添加:

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

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

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

答案 1 :(得分:0)

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

网络服务

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