Windows Communication Foundation (WCF) 在嵌套 WCF 环境中的调用跳转机制






4.80/5 (3投票s)
如何在嵌套 WCF 中实现调用跳转
引言
Windows Communication Foundation (WCF) 提供了一种在网络上托管服务契约的绝佳方式。此外,其内置的自托管和不同协议支持(TCP、HTTP 等)功能使其成为理想的选择。用户可以很好地控制服务及其功能。
WCF 需要三个组件:一个服务器、一个主机和一个客户端。现在,一个简单的调用机制很容易实现。但在当今复杂的业务和软件模型中,很难将每个功能都置于单一阶段。一个系统可能需要进行多次网络调用跳转,服务器可能在内部作为客户端调用另一个服务器,甚至那个另一个服务器也可以作为另一个服务器的客户端。因此,如果每次跳转都通过 WCF 契约,那么在第一阶段之后,调用会遇到技术问题。本文将通过一个小示例来解释这样一个系统,其中包含一些可能遇到的问题以及一个简单的解决方案。
假设:您必须具备 WCF 契约和自托管的基本知识。
问题陈述:当第一个服务器在自托管模型中充当客户端时,对第二个服务器的调用会失败。
背景
首先,让我们理解一个两级架构。假设我们选择通过 BasicHttp 进行自托管。服务器将生成一个配置文件,客户端需要使用该配置文件才能通过主机与服务器进行通信。
假设在服务器端,我们有两个组件:一个是放在“dll”中的服务器端代码。另一个是作为 EXE 的主机,它引用了“dll”,dll 中的每个函数都是客户端可以调用的公开方法。客户端由一个 EXE 和一个由该 EXE 使用的“App.config”文件组成。(此配置需要在运行时绑定连接)。当客户端调用服务器时,服务器“dll”将内部调用另一个自托管的 WCF 服务。为此,服务器需要使用第二个服务器的配置。
WCF 调用跳转
使用代码 - 真实生活示例
让我们从一个例子开始。有一个两级 WCF 架构。一端是客户端,它通过自托管应用程序调用 WCF 函数,反过来,服务器调用另一个 WCF 服务(同样通过自托管应用程序)。客户端应用程序是一个简单的将两个字符串连接起来的应用程序。
客户端应用程序
客户端正在调用一个 WCF 服务来执行任务。客户端代码和契约如下:
private void btnClick_Click(object sender, EventArgs e)
{
try
{
ContractConcat.ConcatClient obj = new ContractConcat.ConcatClient();
tbxResult.Text = obj.Concatenate(tbxFirst.Text, tbxSecond.Text);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
<client>
<endpoint address="http://127.0.0.1:4040/ServerCode" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IConcat" contract="ContractConcat.IConcat"
name="WSHttpBinding_IConcat">
<identity>
<userPrincipalName value="AnantBeriwal-PC\Anant Beriwal" />
</identity>
</endpoint>
</client>
我创建了一个名为 _WCFHost_ 的主机,它托管一个契约。正在托管的契约称为 _IConcat_。主机的 _App.Config_ 具有以下条目,以促进与服务器的正常通信。
<service name="WCFServer.ServerCode">
<endpoint address="http://127.0.0.1:4040/ServerCode" binding="wsHttpBinding" contract="WCFServer.IConcat">
</endpoint>
</service>
如您所见,它具有 _ConcatClient_ 的引用。_ConcatClient_ 通过自托管服务器连接到服务器。服务器契约是一个名为 _WCFServer_ 的项目,其 _ServerCode_ 文件和绑定接口如下:
namespace WCFServer
{
public class ServerCode : IConcat
{
public string Concatenate(string strfirstString, string strSecondString)
{
ContractValidity.CheckForValidityClient obj = new ContractValidity.CheckForValidityClient();
bool ResultFirst = obj.CheckForValidityOfString(strfirstString);
bool ResultSecond = obj.CheckForValidityOfString(strSecondString);
if (ResultFirst && ResultSecond)
{
return strfirstString + strSecondString;
}
else
{
return "Not a Valid Input";
}
}
}
}
namespace WCFServer
{
[ServiceContract]
interface IConcat
{
[OperationContract]
String Concatenate(string strfirstString, string strSecondString);
}
}
如果您仔细查看代码部分,您会发现此服务器代码正在调用 _IValidity_ WCF 服务,因此当客户端调用 Concatenate 时,实际上有两次跳转。第二个服务器称为 _WCFHopServer_,它公开了一个名为 _IValidity_ 的契约。代码示例如下:
namespace WCFHopServer
{
public class HopServerCode : ICheckForValidity
{
public bool CheckForValidityOfString(string strString)
{
for (int i = 0; i < strString.Length; i++)
{
if (char.IsLetter(strString[i]))
{
continue;
}
else
{
return false;
}
}
return true;
}
}
}
namespace WCFHopServer
{
[ServiceContract]
interface ICheckForValidity
{
[OperationContract]
bool CheckForValidityOfString(string strString);
}
}
其相应的宿主将具有这样的条目以促进通信。(实际上,我使用了同一个 EXE 来公开这两个服务,因为它们都在同一台机器上。但这无论如何都不会影响跳转机制。)
<service name="WCFHopServer.HopServerCode">
<endpoint address="http://127.0.0.1:4040/HopServerCode" binding="wsHttpBinding" contract="WCFHopServer.ICheckForValidity">
</endpoint>
</service>
最终架构如下:
WCF 调用跳转
现在我们将运行应用程序。首先,将运行主机。当主机启动时,它将托管这两个服务契约(这是为了方便展示;这两个契约可以分开托管)。
WCFHost
现在,当点击“_Concatenate_”按钮时,您期望什么?它应该能正常工作,还是我们遗漏了什么?等等,出现了一个错误。
原因很简单。_WCFServer_ 没有 _WCFHopServer_ 配置。为什么?因为我们从未提供过。实际上,它并不像看起来那么简单。如果您仔细观察,_WCFServer_ 已经有了一个配置。那就是 _IConcat_ 的配置,由 _WCFHost_ 提供(一个“dll”本身没有任何配置,父 EXE 为其提供)。由于 _WCFHost_ 托管 _IConcat_ 服务,它有一个 app.config,它会传递给 _WCFServer_。现在 _WCFSever_ 如何使用 _WCFHopServer_ 的服务配置?
有三种方法可以做到这一点。
- 将其硬编码到 _WCFServer_ 代码中。绑定类可以为您做到这一点。但这是不理想的。为了方便起见,您可以将所有配置放在另一个文件中,自己读取值,然后初始化绑定类。但代码是额外的开销。
- 其次,如果您不想编写太多代码,ConfigurationManager 类将为您完成大部分工作(读取和管理 app.config,特别是读取配置文件本身的节点)。但您仍然需要自己编写绑定代码并自己调用服务器。另外,维护另一个 app.config 会很麻烦。
- 您可以走第三条路,这也是本文的主题。我们不需要添加任何代码,将所有内容留给配置文件。我们将使用这种技术。
问题在于主机无法提供关于第二个服务器配置的任何信息。但我们可以修改 app.config 来做到这一点。为了分类,在 App.config 中,有两种类型的契约。
- <Services> 定义服务器端契约
- <Client> 定义客户端契约
我们可以将这两个部分保留在同一个配置文件中。让我们看看我们需要的客户端部分。它是一个 <Client> 标签,其中包含 _IValidity_ 契约的详细信息。
<client>
<endpoint address="http://127.0.0.1:4040/HopServerCode" binding="wsHttpBinding"
contract="ContractValidity.ICheckForValidity">
</endpoint>
</client>
将此标签添加到 _WCFHost_ 的 App.Config 中。现在,根据配置文件,_WCFHost_ 同时作为客户端和服务器运行。最终配置如下:
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<services>
<service name="WCFServer.ServerCode">
<endpoint address="http://127.0.0.1:4040/ServerCode" binding="wsHttpBinding" contract="WCFServer.IConcat">
</endpoint>
</service>
<service name="WCFHopServer.HopServerCode">
<endpoint address="http://127.0.0.1:4040/HopServerCode" binding="wsHttpBinding" contract="WCFHopServer.ICheckForValidity">
</endpoint>
</service>
</services>
<client>
<endpoint address="http://127.0.0.1:4040/HopServerCode" binding="wsHttpBinding"
contract="ContractValidity.ICheckForValidity">
</endpoint>
</client>
</system.serviceModel>
</configuration>
我们利用了 EXE 的基本属性,即它会为 dll 的底层提供配置。现在 dll 正在寻找一个客户端契约,但它收到了一个服务器契约。因此问题仍然存在。有了新的 _WCFHost_ 及其配置,客户端将不会抛出任何异常,并将显示结果。
最终结果
就这样,各位。我希望这是一种有用、简单且节省时间的解决方案。
sample code can be downloaded from here- SampleCode.zip