WCF で NHibernate セッションを管理するために使用するモデルは次のとおりです。
1) ICallContextInitializer も実装する System.ServiceModel.ServiceHost から継承する独自の ServiceHost クラスがあります。次のように、サービスの各操作にサービス ホスト インスタンスを追加します。
protected override void InitializeRuntime()
{
base.InitializeRuntime();
foreach (ChannelDispatcher cd in this.ChannelDispatchers)
{
foreach (EndpointDispatcher ed in cd.Endpoints)
{
foreach (DispatchOperation op in ed.DispatchRuntime.Operations)
{
op.CallContextInitializers.Add(this);
}
}
}
}
public void AfterInvoke(object correlationState)
{
// We don't do anything after the invoke
}
public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
{
OperationContext.Current.Extensions.Add(new SessionOperationContext());
return null;
}
BeforeInvoke は、各 WCF 呼び出しの OperationContext に独自のセッションがあることを確認するだけです。IDispatchMessageInspector で、応答のシリアル化中にセッションが利用できないという問題が見つかりました。これは、遅延読み込みを使用する場合の問題です。
2) 次に、SessionOperationContext が呼び出されてアタッチされ、OperationCompleted イベントを使用して自分自身を削除します。このようにして、セッションが応答のシリアル化に使用できることを確認できます。
public class SessionOperationContext : IExtension<OperationContext>
{
public ISession Session { get; private set; }
public static SessionOperationContext Current
{
get
{
OperationContext oc = OperationContext.Current;
if (oc == null) throw new InvalidOperationException("Must be in an operation context.");
return oc.Extensions.Find<SessionOperationContext>();
}
}
public void Attach(OperationContext owner)
{
// Create the session and do anything else you required
this.Session = ... // Whatever instantiation method you use
// Hook into the OperationCompleted event which will be raised
// after the operation has completed and the response serialised.
owner.OperationCompleted += new EventHandler(OperationCompleted);
}
void OperationCompleted(object sender, EventArgs e)
{
// Tell WCF this extension is done
((OperationContext)sender).Extensions.Remove(this);
}
public void Detach(OperationContext owner)
{
// Close our session, do any cleanup, even auto commit
// transactions if required.
this.Session.Dispose();
this.Session = null;
}
}
高負荷のアプリケーションで上記のパターンをうまく使用しており、うまく機能しているようです。
要約すると、これは新しい WcfOperationSessionContext が行うことと似ています (上記のパターンを理解したときには存在しませんでした ;-)) だけでなく、遅延読み込みに関する問題も解決しています。
尋ねられた追加の質問について:上記で概説したモデルを使用する場合、次のことを行うだけです:
void SaveMyEntity(MyEntity entity)
{
SessionOperationContext.Current.Session.Save(entity);
}
セッションが常にそこにあり、WCF 操作が完了すると破棄されることが保証されます。必要に応じて、通常の方法でトランザクションを使用できます。