创建和消费简单 WCF 服务而不使用配置文件






4.94/5 (25投票s)
本文介绍了如何根据提供的参数创建简单的WCF服务并托管它,以及如何创建客户端来使用此服务。
- 下载 WCF 服务库源代码 - 7.7 KB
- 下载可执行客户端应用程序 - 3.8 KB
- 下载可执行宿主应用程序 - 5.3 KB
- 下载客户端应用程序源代码 - 3.6 KB
- 下载宿主应用程序源代码 - 3 KB
引言
本文介绍了如何根据指定的绑定和指定的端口创建简单的WCF服务宿主。所有这些都可以动态完成。
背景
通常,我们在开发WCF服务时,习惯使用配置文件(根据环境不同,可能是web.config/app.config)。使用配置文件确实是一个非常好的主意。这一切看起来都很自动化。而且使用配置文件是默认的做事方式。
使用代码
在本文中,我使用了三个项目
- WCF 服务 (WCFMathLib.dll):实际的服务逻辑,它定义了一个服务契约接口(Service Contract)、操作契约(OperationContract)并实现它们,然后向外界公开几个函数供使用。
- 宿主应用程序 (ConWCFMathHost.exe):定义了根据命令行提供的参数来宿主所述服务的逻辑。
- 客户端应用程序 (ConWCFMathClient.exe):将使用此服务的客户端应用程序。
第一部分:WCF 服务 (WCFMathLib.dll)
实现业务逻辑的实际服务。它定义了一个服务契约(ServiceContract)以及服务提供的一些操作,如下所示。我们称之为IMathInterface.cs。
要创建此项目,您可以从项目向导选项中选择一个类库项目。我们将其命名为“WCFMathLib”。它已经包含了一个文件Class1.cs,将该文件重命名为MathService.cs,再添加一个文件(IMathService.cs)来定义接口,尽管您也可以在此文件中定义接口。下面是两个文件的列表。
服务契约定义
// IMathInterface.cs
[ServiceContract]
public interface IMathService
{
[OperationContract]
double AddNumber(double dblX, double dblY);
[OperationContract]
double SubtractNumber(double dblX, double dblY);
[OperationContract]
double MultiplyNumber(double dblX, double dblY);
[OperationContract]
double DivideNumber(double dblX, double dblY);
}
服务契约实现
// MathInterface.cs
public class MathService : IMathService
{
public double AddNumber(double dblX, double dblY)
{
return (dblX + dblY);
}
public double SubtractNumber(double dblX, double dblY)
{
return (dblX - dblY);
}
public double MultiplyNumber(double dblX, double dblY)
{
return (dblX * dblY);
}
public double DivideNumber(double dblX, double dblY)
{
return ((dblY == 0 ) ? 0 : dblX / dblY);
}
}
ServiceContract 属性
服务契约:描述了哪些相关操作可以绑定在一起作为一个单一的功能单元,供客户端在服务上执行。
OperationContract 属性
操作契约指定该操作由服务公开;服务定义了操作的参数和返回类型。正如我们所见,这里没有什么特别之处,只是一个服务契约定义及其实现。
第二部分:WCF 服务宿主 (ConWCFMathHost.exe)
宿主应用程序被开发为基于控制台的应用程序,它根据提供的参数来宿主我们的服务(WCFMathLib.MathService)。参数的提供形式如下。它接受两个参数:ConWCFMathHost.exe TCP/HTTP portAdr <return>。
- 参数 1:绑定的可接受值为 HTTP 和 TCP。
- 参数 2:端口号,一个整数值,指定端口值。
使用 HTTP 绑定宿主服务
private static void StartHTTPService( int nPort)
{
string strAdr = "https://:" + nPort.ToString() + "/MathService";
try
{
Uri adrbase = new Uri(strAdr);
m_svcHost = new ServiceHost(typeof(MathService), adrbase);
ServiceMetadataBehavior mBehave = new ServiceMetadataBehavior();
m_svcHost.Description.Behaviors.Add(mBehave);
m_svcHost.AddServiceEndpoint(typeof(IMetadataExchange),
MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
BasicHttpBinding httpb = new BasicHttpBinding();
m_svcHost.AddServiceEndpoint(typeof(IMathService), httpb, strAdr);
m_svcHost.Open();
Console.WriteLine("\n\nService is Running as >> " + strAdr );
}
catch (Exception eX)
{
m_svcHost = null;
Console.WriteLine("Service can not be started as >> [" +
strAdr + "] \n\nError Message [" + eX.Message + "]");
}
}
使用 TCP 绑定宿主服务
private static void StartTCPService(int nPort)
{
string strAdr = "net.tcp://:" + nPort.ToString() + "/MathService/";
try
{
Uri adrbase = new Uri(strAdr);
m_svcHost = new ServiceHost(typeof(MathService), adrbase);
NetTcpBinding tcpb = new NetTcpBinding();
ServiceMetadataBehavior mBehave = new ServiceMetadataBehavior();
m_svcHost.Description.Behaviors.Add(mBehave);
m_svcHost.AddServiceEndpoint(typeof(IMetadataExchange),
MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
m_svcHost.AddServiceEndpoint(typeof(IMathService), tcpb, strAdr);
m_svcHost.Open();
Console.WriteLine("\n\nService is Running as >> " + strAdr );
}
catch (Exception eX)
{
m_svcHost = null;
Console.WriteLine("Service can not be started as >> [" +
strAdr + "] \n\nError Message [" + eX.Message + "]");
}
}
由于没有配置文件,我们所要做的(请看粗体部分)基本上是以下几件事:
代码描述
创建所需的绑定(TCP/HTTP)
ServiceMetadataBehavior 控制服务元数据和相关信息的发布。虽然我们可以省略粗体部分,但就本项目而言,我们将手动创建一切,但始终添加一个IMetadataExchange
端点到服务是一个好主意,因为它公开了用于返回服务元数据的方法。这在使用SVCUtil.exe工具从服务生成代理类和配置文件时非常有用。
第三部分:客户端 (ConWCFMathClient.exe)
客户端应用程序被开发为基于控制台的应用程序,通过命令行以以下形式传递参数。您需要在命令行中提供六个参数。
ConWCFMathClient <Host Machine> <TCP/HTTP> <Port#> <ADD/SUB/MUL/DIV> Num1 Num2
- 参数 1:托管服务的计算机的名称/IP 地址。
- 参数 2:托管服务的绑定,可接受的值是HTTP 和 TCP。
- 参数 3:端口号,一个整数值,指定端口值。
- 参数 4:操作代码(可接受的值是ADD, SUB, MUL, DIV)。
- 参数 5:操作数 1,操作的操作数 1 值。
- 参数 6:操作数 2,操作的操作数 2 值。
导入接口定义
您可以定义接口,或者直接从服务中导入IMathService.cs文件。这只是定义了接口,以避免在编译客户端项目时出现错误。
// Listing of IMathService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace ConWCFMathClient
{
[ServiceContract]
public interface IMathService
{
[OperationContract]
double AddNumber(double dblX, double dblY);
[OperationContract]
double SubtractNumber(double dblX, double dblY);
[OperationContract]
double MultiplyNumber(double dblX, double dblY);
[OperationContract]
double DivideNumber(double dblX, double dblY);
}
}
调用服务的方法
创建了一个函数来调用服务的方法,在验证所有参数后将它们传递给方法。这是该方法
private static void Evaluate(string strServer, string strBinding,
int nPort, string strOper, double dblVal1, double dblVal2)
{
ChannelFactory<IMathService> channelFactory = null;
EndpointAddress ep = null;
string strEPAdr = "http://" + strServer +
":" + nPort.ToString() + "/MathService";
try
{
switch (strBinding)
{
case "TCP":
NetTcpBinding tcpb = new NetTcpBinding();
channelFactory = new ChannelFactory<IMathService>(tcpb);
// End Point Address
strEPAdr = "net.tcp://" + strServer + ":" +
nPort.ToString() + "/MathService";
break;
case "HTTP":
BasicHttpBinding httpb = new BasicHttpBinding();
channelFactory = new ChannelFactory<IMathService>(httpb);
// End Point Address
strEPAdr = "http://" + strServer + ":" +
nPort.ToString() + "/MathService";
break;
}
// Create End Point
ep = new EndpointAddress(strEPAdr);
// Create Channel
IMathService mathSvcObj = channelFactory.CreateChannel(ep);
double dblResult = 0;
// Call Methods
switch (strOper)
{
case "ADD": dblResult = mathSvcObj.AddNumber(dblVal1, dblVal2); break;
case "SUB": dblResult = mathSvcObj.SubtractNumber(dblVal1, dblVal2); break;
case "MUL": dblResult = mathSvcObj.MultiplyNumber(dblVal1, dblVal2); break;
case "DIV": dblResult = mathSvcObj.DivideNumber(dblVal1, dblVal2); break;
}
// Display Results.
Console.WriteLine("Operation {0} ", strOper );
Console.WriteLine("Operand 1 {0} ", dblVal1.ToString ( "F2"));
Console.WriteLine("Operand 2 {0} ", dblVal2.ToString("F2"));
Console.WriteLine("Result {0} ", dblResult.ToString("F2"));
channelFactory.Close();
}
catch (Exception eX)
{
// Something unexpected happended ..
Console.WriteLine("Error while performing operation [" +
eX.Message + "] \n\n Inner Exception [" + eX.InnerException + "]");
}
}
代码描述
在调用任何方法之前,您需要做两件事:创建一个通道工厂对象,使用指定的绑定,通过
对于 HTTP 绑定
BasicHttpBinding httpb = new BasicHttpBinding();
channelFactory = new ChannelFactory<IMathService>(httpb);
对于 TCP 绑定
NetTcpBinding tcpb = new NetTcpBinding();
channelFactory = new ChannelFactory<IMathService>(tcpb);
HTTP 绑定的端点地址
strEPAdr = "http://" + strServer + ":" + nPort.ToString() + "/MathService";
TCP 绑定的端点地址
strEPadr = strEPAdr = "net.tcp://" + strServer + ":" + nPort.ToString() + "/MathService";
通过指定的端点地址创建通道
// Create End Point
ep = new EndpointAddress(strEPAdr);
// Create Channel
IMathService mathSvcObj = channelFactory.CreateChannel(ep);
通过通道调用方法并显示输出
double dblResult = 0;
// Call Methods
switch (strOper)
{
case "ADD": dblResult = mathSvcObj.AddNumber(dblVal1, dblVal2); break;
case "SUB": dblResult = mathSvcObj.SubtractNumber(dblVal1, dblVal2); break;
case "MUL": dblResult = mathSvcObj.MultiplyNumber(dblVal1, dblVal2); break;
case "DIV": dblResult = mathSvcObj.DivideNumber(dblVal1, dblVal2); break;
}
// Display Results.
Console.WriteLine("Operation {0} ", strOper );
Console.WriteLine("Operand 1 {0} ", dblVal1.ToString ( "F2"));
Console.WriteLine("Operand 2 {0} ", dblVal2.ToString("F2"));
Console.WriteLine("Result {0} ", dblResult.ToString("F2"));
// Close channel
channelFactory.Close();
关注点
- 无配置文件。
- 未使用 SvcUtil 生成代理。
输出
使用 TCP 端点运行服务
客户端
使用 HTTP 端点运行服务
注意:在 HTTP 模式下运行服务时,您需要以管理员模式打开命令提示符。
客户端
关注点
在本文中,我用非常简单的方式描述了所有三种契约类型,也解释了每种契约类型的适用性。何时应该使用。希望您会喜欢阅读它,就像我喜欢写它一样。
非常感谢您的阅读。
历史
初始版本。