WCF是各种分布式技术的集大成者
WCF有服务不能孤立地存在,需要寄宿于一个运行着的进程中,我们把承载WCF服务的进程称为宿主,为服务指定宿主的过程称为服务寄宿(Service Hosting)。
我们采用自我寄宿(Self-Hosting)和IIS寄宿两种寄宿方式。
自我寄宿
通过一个控制台应用作为服务的宿主;
IIS寄宿
寄宿于IIS的工作进程W3WP.exe
客户端通过另一个控制台应用模拟(进程为Client)。
步骤一:构建整个解决方案
通过vs2015创建一个空白的解决方案,起名为WcfServices并添加如下4个项目
Service.Interface:用于定义服务契约Servcie Contract的类库项目,引用WCF的核心程序集System.ServiceModel.dll。
Service:用于定义服务类型的类库项目。由于服务类型需要实现定义在Service.Interface中相应的契约接口,因此该项目具有对Service.Interface项目的引用。
Hosting:作为服务宿主的控制台应用。该项目同时引用service.interface、Service项目和System.ServcieModel.dll程序集。
Client:一个控制台应用程序模拟服务的客户端,该项目引用System.ServcieModel.dll程序集。
步骤二:创建服务契约
从功能上来讲,服务契约抽象了服务提供的所有操作;而站在消息交换的角度来看,服务契约则定义了基于服务调用的消息交换过程中请求消息和回复消息的结构,以及采用的消息交换模式,我们一般将服务契约定义成接口。
我们通过将System.ServiceModel.ServiceContractAttribute特性应用在接口ICalculator上从而将其定义成了服务契约。在ServiceContractAttribute特性的同时,还可以指定服务契约的名称(CalculatorService)和命名空间(https://blog.csdn.net/zang141588761)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceModel;
namespace WcfServices.Service.Interface
{
[ServiceContract(Name ="CaculatorService",Namespace = "https://blog.csdn.net/zang141588761")]
public interface ICalculator
{
[OperationContract]
double Add(double x, double y);
[OperationContract]
double Subtract(double x, double y);
[OperationContract]
double Multiply(double x, double y);
[OperationContract]
double Divide(double x, double y);
}
}
通过serviceContractAttribute特性将接口定义成服务契约之后,接口的方法并不能自动生成服务操作。WCF采用的是选择(Explicit Opt-in)的策略,意味着我们需要在相应的操作方法上面显式地应用OperationContractAttribute特性。
步骤三创建服务
在Service项目中他都实现ICaculator的服务CaculatorService。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WcfServices.Service.Interface;
namespace WcfServices.Service
{
public class CaculatorService : ICalculator
{
public double Add(double x, double y)
{
return x + y;
}
public double Divide(double x, double y)
{
return x / y;
}
public double Multiply(double x, double y)
{
return x * y;
}
public double Subtract(double x, double y)
{
return x - y;
}
}
}
步骤四通过自我寄宿的方式寄宿服务
WCF服务需要依存一个运行着的宿主进程,服务寄宿就是为了服务指定一个宿主的过程。WCF采用基于终点Endpoint的通信手段。
地址 Address:
地址决定了服务的位置,解决了服务寻址的问题
绑定 Binding
绑定实现通信的所有细节,包括网络传输、消息编码,以及其他为实现某种功能对消息进行的相应处理。
契约 Contract
是对服务操作的抽象,也是对消息交换模式及消息结构的定义。
三要素组成。三个首字母分别为ABC Endpoint=ABC
服务寄宿的目的就是开启一个进程,为WCF服务提供一个运行的环境,并为服务添加一个或多个终结点,使之暴露给潜在的服务消费者。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceModel;
using System.ServiceModel.Description;
using WcfServices.Service;
using WcfServices.Service.Interface;
namespace WcfServices.Hosting
{
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(CaculatorService)))
{
host.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(),
"http://127.0.0.1:3721/CauculatorService");
if (host.Description.Behaviors.Find<ServiceMetadataBehavior>()==null)
{
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true;
behavior.HttpGetUrl = new Uri("http://127.0.0.1:3721/CaculatorService/metadata");
host.Description.Behaviors.Add(behavior);
}
host.Opened += delegate
{
Console.WriteLine("CaculatorService已经启动,按任意键终止服务!");
};
host.Open();
Console.Read();
}
}
}
}
寄宿代码中,我们为ServiceHost添加了ServiceMetadataBehavior这样一个服务行为,并采用了基于HTTP-GET的元数据获取方式,并且通过ServiceMetadataBehavior的HttpGetUrl属性指定元数据发
布地址http://127.0.0.1:3721/caculatorService/metadata。的服务成功寄宿后,可以通过该地址获取到相关的元数据。如下:
在进行真正的WCF应用开发时,一般采用配置而不是编程的方式进行终点的添加和服务行为的定义。
如下:
<configuration>
<system.servcieModel>
<behaviors>
<serviceBehaviors>
<behavior name="metadataBehavior">
<serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:3721/caculatorservice/metadata"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="WcfServices.Service.CalculatorService" behaviorConfiguration="metadataBehavior">
<endpoint address="http://127.0.0.1:3721/calculatorservice" binding="wsHttpBinding"
contract="WcfServices.Service.Interface.ICalculator"/>
</service>
</services>
</system.servcieModel>
</configuration>
可用Tools-->WCF服务配置编辑器来编辑
步骤五创建客户端调用:
服务被成功寄宿后,服务端便开始了服务调用请求监听。当通过Visual Studio添加服务引用的时候,Visual Stu,dio在内部帮助我们实现元数据的获取,并借助这些元数据通过代码生成工具SvcUtil.exe自动生成用于服务调用的服务代码相关代码和相应的配置。
在运行服务寄宿Hosting.exe的情况下,右键单击Client项目并在弹出的快捷菜间中选择“添加服务引用”。在地址栏输入服务元数据发的布的源地址http://127.0.0.1:3721/caculatorservice/metadata,并指定一个命名空间,单击OK按钮。
当服务引用被成功添加后,被客户端用于进行服务调用的服务契约接口CalculatorService会被生成出来。客户端的契约接口之所以会命名为CalculatorService,而不是ICalculator,是因为我们在定义接口契约接口的时候将ServiceContractAttribute特性的Name属性设置成了CalculatorService。CaculatorService是与定义在service.interface项目中的ICalculator接口等效的契约的接口。
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace Client.ServiceReferences {
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="https://blog.csdn.net/zang141588761", ConfigurationName="ServiceReferences.CaculatorService")]
public interface CaculatorService {
[System.ServiceModel.OperationContractAttribute(Action="https://blog.csdn.net/zang141588761/CaculatorService/Add", ReplyAction="https://blog.csdn.net/zang141588761/CaculatorService/AddResponse")]
double Add(double x, double y);
[System.ServiceModel.OperationContractAttribute(Action="https://blog.csdn.net/zang141588761/CaculatorService/Add", ReplyAction="https://blog.csdn.net/zang141588761/CaculatorService/AddResponse")]
System.Threading.Tasks.Task<double> AddAsync(double x, double y);
[System.ServiceModel.OperationContractAttribute(Action="https://blog.csdn.net/zang141588761/CaculatorService/Subtract", ReplyAction="https://blog.csdn.net/zang141588761/CaculatorService/SubtractResponse")]
double Subtract(double x, double y);
[System.ServiceModel.OperationContractAttribute(Action="https://blog.csdn.net/zang141588761/CaculatorService/Subtract", ReplyAction="https://blog.csdn.net/zang141588761/CaculatorService/SubtractResponse")]
System.Threading.Tasks.Task<double> SubtractAsync(double x, double y);
[System.ServiceModel.OperationContractAttribute(Action="https://blog.csdn.net/zang141588761/CaculatorService/Multiply", ReplyAction="https://blog.csdn.net/zang141588761/CaculatorService/MultiplyResponse")]
double Multiply(double x, double y);
[System.ServiceModel.OperationContractAttribute(Action="https://blog.csdn.net/zang141588761/CaculatorService/Multiply", ReplyAction="https://blog.csdn.net/zang141588761/CaculatorService/MultiplyResponse")]
System.Threading.Tasks.Task<double> MultiplyAsync(double x, double y);
[System.ServiceModel.OperationContractAttribute(Action="https://blog.csdn.net/zang141588761/CaculatorService/Divide", ReplyAction="https://blog.csdn.net/zang141588761/CaculatorService/DivideResponse")]
double Divide(double x, double y);
[System.ServiceModel.OperationContractAttribute(Action="https://blog.csdn.net/zang141588761/CaculatorService/Divide", ReplyAction="https://blog.csdn.net/zang141588761/CaculatorService/DivideResponse")]
System.Threading.Tasks.Task<double> DivideAsync(double x, double y);
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public interface CaculatorServiceChannel : Client.ServiceReferences.CaculatorService, System.ServiceModel.IClientChannel {
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public partial class CaculatorServiceClient : System.ServiceModel.ClientBase<Client.ServiceReferences.CaculatorService>, Client.ServiceReferences.CaculatorService {
public CaculatorServiceClient() {
}
public CaculatorServiceClient(string endpointConfigurationName) :
base(endpointConfigurationName) {
}
public CaculatorServiceClient(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
}
public CaculatorServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
}
public CaculatorServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress) {
}
public double Add(double x, double y) {
return base.Channel.Add(x, y);
}
public System.Threading.Tasks.Task<double> AddAsync(double x, double y) {
return base.Channel.AddAsync(x, y);
}
public double Subtract(double x, double y) {
return base.Channel.Subtract(x, y);
}
public System.Threading.Tasks.Task<double> SubtractAsync(double x, double y) {
return base.Channel.SubtractAsync(x, y);
}
public double Multiply(double x, double y) {
return base.Channel.Multiply(x, y);
}
public System.Threading.Tasks.Task<double> MultiplyAsync(double x, double y) {
return base.Channel.MultiplyAsync(x, y);
}
public double Divide(double x, double y) {
return base.Channel.Divide(x, y);
}
public System.Threading.Tasks.Task<double> DivideAsync(double x, double y) {
return base.Channel.DivideAsync(x, y);
}
}
}
真正被客户端用于服务调用的则是一个叫做CalculatorServiceClient的类。CalculatorServiceClient的基类为System.ServiceModel.ClientBase<CalculatorService>。CalculatorServiceClient同样实现了契约接口service.interface,并通过调用从基类继承的Channel属性的相应方法实现了4个运算操作。
客户端实现如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Client.ServiceReferences;
namespace Client
{
class Program
{
static void Main(string[] args)
{
using (CaculatorServiceClient proxy = new CaculatorServiceClient())
{
Console.WriteLine("x+y={2} when x = {0} and y = {1}",1,2,proxy.Add(1,2));
Console.WriteLine("x-y={2} when x = {0} and y = {1}", 1, 2, proxy.Subtract(1, 2));
Console.WriteLine("x*y={2} when x = {0} and y = {1}", 1, 2, proxy.Multiply(1, 2));
Console.WriteLine("x/y={2} when x = {0} and y = {1}", 1, 2, proxy.Divide(1, 2));
}
Console.Read();
}
}
}
输出结果如下:
源码地址:https://download.csdn.net/download/zang141588761/11107731