2012-04-27 163 views
0

我有一個C#.Net Web服務。我正在調用一個使用nHibernate連接到我的數據庫的dll(C#.Net)。當我調用dll時,它會執行對數據庫的查詢並加載父對象「任務」。然而,當DLL試圖訪問子對象「Task.SubTasks」,它引發以下錯誤:Web服務nHibernate SessionFactory問題

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

回答

1

您已明顯將啓用延遲加載(或更好:未禁用,因爲它是默認行爲)的NHibernate數據類中的一個集合映射到其中一個。 NHibernate加載實體併爲映射集合創建一個代理。只要他們被訪問,NHibernate嘗試加載該集合的項目。但是如果你在這之前關閉你的N​​Hibernate會話,你會收到錯誤。您可能通過Web服務將您的數據對象展示給Web服務客戶端。在序列化過程中,XmlSerializer嘗試序列化提示NHibernate填充它的集合。會話關閉時發生錯誤。

兩種方法可以防止此:

  • 關閉響應發送

  • 禁用延遲加載您的收藏後的會話,讓他們立即被加載

在上述編輯之後添加:

在您的存儲庫中,您在using語句中啓動UnitsOfWork。代碼完成後即將處理。我不知道UnitOfWork的實現,但我認爲它控制着NHibernate會話的生命週期。通過部署UnitOfWork,你的probalby關閉NHibernate會話。當您的映射初始化延遲加載的集合時,這些集合尚未填充且發生錯誤。 NHibernate需要加載實體來填充延遲初始化集合的會話的確切實例。

如果您使用延遲加載並且有一個在響應完成之前關閉會話的存儲庫,您將遇到類似問題。一種選擇是在請求開始時初始化UnitOfWork,並在響應完成後將其關閉(例如,在Application.BeginRequest,Global.asax.cs中的Application_EndRequest中)。這當然意味着將您的資源庫緊密集成到Web服務中。

在任何情況下,結合延遲加載爲單個請求創建會話是一個壞主意,並且很可能在將來創建類似的問題。如果您無法更改存儲庫實現,則可能必須禁用延遲加載。

+0

感謝您的意見。但是,我認爲我的問題處於更高層次。我沒有任何nHibernate會話代碼。我使用溫莎打開一個容器,但不是一個會話。引發錯誤的代碼是在dll中。 Web服務初始化容器,然後調用dll。 dll從db獲取對象並將它們作爲byte []返回給Web服務,但在它到達之前它正在進行barfing。 – MikeTWebb 2012-04-27 17:05:17

+0

但在某個地方有人正在打開和關閉NHibernate會話。由於錯誤指出NHibernate抱怨一個封閉的會話,它一定是由某人打開和關閉的。溫莎的哪些部分在使用? ActiveRecord,WCF工具,NHibernate集成? – 2012-04-27 17:11:40

+0

表示同意......它在某處「自動」發生。這是nHibernate集成。 hibernate.cfg有一個部分。我已經在上面添加了CreateContainer代碼 – MikeTWebb 2012-04-27 17:27:37

0

使用加蘭的反饋我解決了這個問題。我刪除從DLL庫中的UnitOfWork(S)代碼和包裹Web服務調用在以下的UnitOfWork見代碼MODS的DLL:在DLL

Web服務

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