【发布时间】:2012-03-14 16:23:39
【问题描述】:
我正在将 WCF 实现到现有应用程序中,并且作为实现的一部分,我创建了一个名为 Manager 的单例服务,该服务调用并实例化另一个服务(我们将其称为 LegacyApp)。 LegacyApp 是每会话服务,无非是将现有应用程序实例化为一个整体对象(这种设计是由于现有应用程序的架构造成的)。
这个想法是任何客户端(例如 .NET、Java)都可以连接到 Manager 服务,获取预先存在的 LegacyApp 实例的列表,然后连接到现有实例或获取新实例。
我已经根据MSDN examples 实现了 InstanceContextSharing,但这似乎仅适用于每个应用程序,即如果我在 Manager 服务上创建多个 LegacyApp 客户端,Manager 服务可以通过传递在不同的 LegacyApp 上下文之间切换在具有正确唯一会话 ID 的自定义标头中。但是,如果我尝试使用具有相同唯一会话 ID 的新客户端连接到 LegacyApp,LegacyApp 服务的行为就像没有其他客户端连接一样。
我的问题是:如何在 WCF 中实现不同应用程序之间的会话共享?
编辑(1) Manager 服务和 LegacyApp 服务由同一服务主机托管。 Manager 服务使用代理客户端(在单独的代理库中定义)连接到 LegacyApp 服务。从某种意义上说,服务主机本身就是一个客户端,因为客户端和服务器都在同一个服务主机中。如果我创建另一个使用同一代理库创建代理客户端的客户端(例如 WinForms 客户端),那么我会得到上面描述的行为。
下面是我实现的一些sn-ps代码。扩展类和 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
经理服务:
<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 服务的包装器?
【问题讨论】:
-
目前还不清楚“调用和实例化另一个服务(我们称之为 LegacyApp)”将如何实现。您是说旧版应用是管理器通过代理调用的单独 WCF 服务吗?
-
我很抱歉没有提供足够的细节。我已经编辑了原始问题以包含相关代码的简要概述。感谢您在尝试了解其工作原理方面的帮助!