0

C#.NetWebサービスを使用しています。nHibernateを使用してデータベースに接続するdll(C#.Net)を呼び出しています。dllを呼び出すと、dbへのクエリが実行され、親オブジェクト「タスク」が読み込まれます。ただし、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データクラスの1つにコレクションをマッピングしたようです(または、デフォルトの動作であるため、無効にしないでください)。NHibernateはエンティティをロードし、マップされたコレクションのプロキシを作成します。それらがアクセスされるとすぐに、NHibernateはそのコレクションのアイテムをロードしようとします。ただし、それが発生する前にNHibernateセッションを閉じると、受け取ったエラーが発生します。おそらく、Webサービスを介してデータオブジェクトをWebサービスクライアントに公開しています。シリアル化プロセス中に、XmlSerializerはコレクションのシリアル化を試み、NHibernateにコレクションへの入力を促します。セッションが閉じられると、エラーが発生します。

これを防ぐ2つの方法:

  • 応答が送信された後、セッションを閉じます

また

  • コレクションの遅延読み込みを無効にして、コレクションがすぐに読み込まれるようにします

上記の編集後の追加:

リポジトリで、usingステートメント内でUnitsOfWorkを開始します。コードが完成するとすぐに破棄されます。UnitOfWorkの実装はわかりませんが、NHibernateセッションの存続期間を制御していると思います。UnitOfWorkを破棄することで、NHibernateセッションを閉じることができます。マッピングが遅延ロードされたコレクションを初期化するため、これらのコレクションはまだ入力されておらず、エラーが発生します。NHibernateは、遅延初期化されたコレクションにデータを取り込むために、エンティティをロードしたセッションの正確なインスタンスを必要とします。

遅延読み込みを使用し、応答が完了する前にセッションを閉じるリポジトリがある場合、このような問題が発生します。1つのオプションは、要求の開始時にUnitOfWorkを初期化し、応答が完了した後にそれを閉じることです(たとえば、Global.asax.csのApplication_BeginRequest、Application_EndRequest)。もちろん、これはリポジトリをWebサービスに緊密に統合することを意味します。

いずれにせよ、遅延読み込みと組み合わせて単一のリクエストのセッションを作成することは悪い考えであり、将来同様の問題を引き起こす可能性が非常に高くなります。リポジトリの実装を変更できない場合は、遅延読み込みを無効にする必要があります。

于 2012-04-27T16:59:51.263 に答える
0

ガーランドのフィードバックを使用して、問題を解決しました。DLLのリポジトリからUnitOfWork(s)コードを削除し、UnitOfWorkでDLLへのWebサービス呼び出しをラップしました。以下のコードmodを参照してください。

ウェブサービス

[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 に答える