【问题标题】:How do I Implement InstanceContextSharing in WCF Between Applications?如何在应用程序之间的 WCF 中实现 InstanceContextSharing?
【发布时间】: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 服务吗?
  • 我很抱歉没有提供足够的细节。我已经编辑了原始问题以包含相关代码的简要概述。感谢您在尝试了解其工作原理方面的帮助!

标签: .net wcf


【解决方案1】:

实例上下文共享是单进程的特性。您可能正在寻找的是durable context,例如存储在数据库中。

【讨论】:

  • 感谢您的回复!不幸的是,我不确定这是否会提供我正在寻找的行为。具体来说,Durable Instance Context 提到 请务必注意,购物车应用程序(在服务器上)在我们断开连接时不会维护购物车实例。“LegacyApp”服务是长期运行的模拟,即使没有活动的客户端调用它的方法,我也需要它继续执行。这种行为是我们选择尝试实现可以​​调用 LegacyApp 服务并保持活动状态的 Manager 服务的另一个原因。
  • 如果我对您的理解正确,那么我当前的实现将无法正常工作。我需要我长期存在的 Manager 服务来代表其他客户端执行实例上下文共享/切换,因为它对 LegacyApp 服务的所有调用都在同一个进程中。对吗?
  • 在这种情况下,它超出了 WCF 的范围,因为这样长时间运行的应用程序必须是单独的 Windows 服务,而不仅仅是您由 WCF 触发的东西。
【解决方案2】:

对于那些对我们的解决方案感兴趣的人,我们通过实施isolated service host architecture 解决了这个问题。我们的解决方案是让 Manager 服务提供多个操作来实例化、查询和关闭多个 LegacyApp 服务,每个服务都隔离在自己的 AppDomain 中(由于在 LegacyApp 中大量使用全局变量,这是必要的)。每个 LegacyApp 服务都作为一个单例实例托管,因此任何希望共享相同 LegacyApp 会话的客户端只需向 Manager 服务查询地址,然后连接到正确的实例。这个解决方案对我们很有效,因为 LegacyApp 无论如何都表现为单例。

另外,非常感谢 Matt Brindley 让 his code 查询本地机器以获取下一个可用的 TCP 端口。这段代码用于Manager服务中StartInstance操作的实现。

再次感谢 Ladislav Mrnka 让我们直接了解 WCF 实例上下文共享。

部分代码sn-ps如下:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-09
    相关资源
    最近更新 更多