2012-03-14 6 views
1

私は既存のアプリケーションにWCFを実装しています。実装の一環として、別のサービス(LegacyAppと呼ぶ)を呼び出してインスタンス化するManagerというシングルトンサービスを作成しました。 LegacyAppはセッション単位のサービスであり、モノリシックオブジェクトとしてインスタンス化された既存のアプリケーション以上のものではありません(この設計は既存のアプリケーションのアーキテクチャによるものです)。アプリケーション間でWCFでInstanceContextSharingを実装するにはどうすればよいですか?

任意のクライアント(.NET、Javaなど)がManagerサービスに接続し、既存のLegacyAppインスタンスのリストを取得し、既存のインスタンスに接続するか、新しいインスタンスを取得できるという考えがあります。

MSDN examplesごとにInstanceContextSharingを実装しましたが、これはアプリケーション単位でしか動作しません。つまり、Managerサービスで複数のLegacyAppクライアントを作成した場合、Managerサービスは異なるLegacyAppコンテキスト間を切り替えることができます固有のセッションIDを持つカスタムヘッダーただし、同じユニークなセッションIDを持つ新しいクライアントを使用してLegacyAppに接続しようとすると、他のクライアントが接続されていないかのようにLegacyAppサービスが動作します。

私の質問は次のとおりです。異なるアプリケーション間で動作するWCFでセッション共有を実装するにはどうすればよいですか?

編集(1) ManagerサービスとLegacyAppサービスが同じサービスホストでホストされています。マネージャサービスは、プロキシクライアント(別のプロキシライブラリで定義されています)を使用してLegacyAppサービスに接続します。ある意味では、サービスホストは、クライアントとサーバーの両方が同じサービスホストにあるため、それ自体のクライアントです。この同じプロキシライブラリを使用してプロキシクライアントを作成する別のクライアント(WinFormsクライアント)を作成すると、上記の動作が発生します。

以下は、私が実装したコードの抜粋です。拡張クラスとShareable属性クラスは、MSDNの例と同じです。簡潔にするために、私は様々なサービスとクライアントの無関係な部分を除外しました。

LegacyAppサービス:

<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerSession)> 
<Shareable()> 
Public Class LegacyAppService 
    Implements ILegacyApp 

    Private _foo As String = "Not Set" 

    Public Sub SetFoo(ByVal foo As String) Implements ILegacyApp.SetFoo 
     _foo = foo 
    End Sub 

    Public Function GetFoo() As String Implements ILegacyApp.GetFoo 
     Return _foo 
    End Function 

End Class 

LegacyAppクライアント:

Public Class LegacyAppClient 
    Inherits ClientBase(Of ILegacyApp) 
    Implements ILegacyApp 

    Public Sub New() 
     Me.New(String.Empty) 
    End Sub 

    Public Sub New(ByVal id As String) 
     If IsNothing(id) OrElse id = String.Empty Then 
      _uniqueID = NewInstanceId() 
     Else 
      _uniqueID = id 
     End If 
     CreateContextHeader() 
    End Sub 

    Private Shared Function NewInstanceId() As String 
     Dim random As Byte() = New Byte(CInt(256/8 - 1)) {} 
     RandomNumberGenerator.GetBytes(random) 
     Return Convert.ToBase64String(random) 
    End Function 

    Private Sub CreateContextHeader() 
     _contextHeader = MessageHeader.CreateHeader(CustomHeader.HeaderName, CustomHeader.HeaderNamespace, _uniqueID) 
    End Sub 

    Public ReadOnly Property ID() As String 
     Get 
      Return _uniqueID 
     End Get 
    End Property 

    Public Sub SetFoo(ByVal foo As String) Implements ILegacyApp.SetFoo 
     Using New OperationContextScope(InnerChannel) 
      OperationContext.Current.OutgoingMessageHeaders.Add(_contextHeader) 
      Channel.SetFoo(foo) 
     End Using 
    End Sub 

    Public Function GetFoo() As String Implements ILegacyApp.GetFoo 
     Using New OperationContextScope(InnerChannel) 
      OperationContext.Current.OutgoingMessageHeaders.Add(_contextHeader) 
      Return Channel.GetFoo() 
     End Using 
    End Function 

End Class 

Managerサービス:

<ServiceBehavior(InstanceContextMode:=InstanceContextMode.Single)> 
Public Class ManagerService 
    Implements IManager 

    Public Function CreateService() As String Implements IManager.CreateService 
     Dim openError As Integer 
     Dim legacyAppClient As New LegacyAppClient 
     legacyAppClient.SetFoo(legacyAppService.ID) 
     Return legacyAppClient.ID 
    End Function 

End Class 

テストクライアント:

<...> 
Dim sessionID As String 
Dim legacyApp As LegacyAppClient 
sessionID = _managerClient.CreateService() 
legacyAppClient = New LegacyAppClient(sessionID) 
Dim foo As String = legacyAppClient.GetFoo() 
<...> 

fooを照会するときにsessionIDの値を返すことが予想されますが、テストクライアントで呼び出されたlegacyAppClient.GetFoo()メソッドが「未設定」を返しています。

編集(2) 私はこの問題について誤って考えていますか?テストクライアントからのすべての呼び出しをManagerサービスに送り、それをLegacyAppサービスへのラッパーとして機能させる必要がありますか?

+0

それはどのように明確ではありません「コールと(私たちはLegacyAppを呼ぶことにします)別のサービスがインスタンス化」実施されます。レガシーアプリケーションが、マネージャがプロキシを介して呼び出す別のWCFサービスであるとお考えですか? –

+0

十分な詳細を提示しなかったことを謝罪します。元の質問を編集して、問題のコードの簡単な概要を含めました。どのようにこの作品を理解しようとあなたの助けに感謝! – dthagard

答えて

0

私たちのソリューションに興味のある方は、isolated service host architectureを実装することで問題を解決しました。私たちのソリューションは、マネージャサービスが、それぞれが独自のAppDomain内で隔離された複数のLegacyAppサービスをインスタンス化、クエリ、および閉じるためのいくつかの操作を提供することでした(これはLegacyAppのグローバル変数が多用されるため必要でした)。各LegacyAppサービスはシングルトンインスタンスとしてホストされるため、同じLegacyAppセッションを共有するクライアントは、マネージャサービスにそのアドレスを照会し、適切なインスタンスに接続するだけで済みます。 LegacyAppはシングルトンとして動作するため、このソリューションはうまく機能します。

また、his codeのMatt Brindleyさんに次の利用可能なTCPポートをローカルマシンに問い合わせることに大きな感謝をします。このコードは、ManagerサービスでStartInstance操作の実装に使用されます。

WCFインスタンスのコンテキスト共有で私たちをまっすぐに設定してくれたLadislav Mrnkaにもう一度感謝します。

いくつかのコードスニペットは以下の通りです:

iManagerのインタフェース:

Imports System.ServiceModel 

<ServiceContract()> 
Public Interface IManager 

    <OperationContract()> 
    Function GetOpenInstances() As String() 

    <OperationContract()> 
    Function StartInstance() As String 

    <OperationContract()> 
    Sub CloseInstance(ByVal address As String) 

End Interface 
1

インスタンスコンテキストの共有は、単一プロセスの機能です。おそらくあなたが探しているものはdurable contextがデータベースに格納されています。

+0

返信いただきありがとうございます!残念ながら、これが私が探している振る舞いを提供するかどうかはわかりません。具体的には、耐久性のあるインスタンスのコンテキスト_私たちが切断されている間、ショッピングカートのアプリケーションがショッピングカートのインスタンスを維持しないことに注意することが重要です。_ "LegacyApp"サービスは長時間実行されるシミュレーションであり、そのメソッドを呼び出すアクティブなクライアントがない場合でも実行を続行します。この動作は、LegacyAppサービスを呼び出して保持できるマネージャーサービスを試して実装しようとしたもう一つの理由です。 – dthagard

+0

私があなたを正しく理解していれば、私の現在の実装はうまくいかないでしょう。 LegacyAppサービスへのすべての呼び出しが同じプロセス内にあるので、他のクライアントに代わってインスタンスコンテキストの共有/切り替えを実行するには、私の長年のマネージャーサービスが必要です。正しい? – dthagard

+0

このような場合は、WCFによってトリガーされるものだけでなく、そのような長時間実行されるアプリケーションが個別のWindowsサービスでなければならないため、WCFの範囲外です。 –

関連する問題