1、WCF 几种服务对象实例创建模式
wcf中有三种服务对象实例创建模式:PerCall、PerSession、Single.(具体在哪里设置,最后例子中有很好的阐述)
2、几种模式下服务对象实例的生命周期(这里暂不考虑Mep)
PerCall模式
PerCall模式工作流程如下
- 客户端创建代理对象(Proxy)
- 客户端调用代理对象的一个契约操作,代理对象将其传递给服务宿主程序
- 宿主应用程序创建一新的服务契约对象,并且执行请求操作
- 在执行完请求操作后,如果要求有应答,那么服务契约会给代理对象一个应答,然后销毁自己
示例代码展示
服务契约代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace Contract
{
[ServiceContract(Name="CalculatorService",Namespace="http://www/gxs.com")]
public interface ICalculator:IDisposable
{
[OperationContract(Name = "CalculatorAdd")]
double Add(double x, double y);
[OperationContract(Name = "CalculatorSubtract")]
double Subtract(double x, double y);
}
}
服务代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Contract;
using System.ServiceModel;
namespace Service
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class CalculatorPerCall:ICalculator
{
public CalculatorPerCall()
{
Console.WriteLine("Create a new instance!!!");
}
public double Add(double x, double y)
{
Console.WriteLine("Call [Add] Method");
return x + y;
}
public double Subtract(double x, double y)
{
Console.WriteLine("Call [Subtract] Method");
return x - y;
}
public void Dispose()
{
Console.WriteLine("The instance has been disposed!!!");
}
}
}
服务端宿主代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using Contract;
using Service;
using System.ServiceModel.Description;
namespace Hosting
{
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(CalculatorPerCall)))
{
host.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "http://127.0.0.1:8089/CalculatorService");
//ServiceMetadataBehavior:控制服务元数据和相关信息的发布
if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
{
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true;
behavior.HttpGetUrl = new Uri("http://127.0.0.1:8089/CalculatorService/metadata");
host.Description.Behaviors.Add(behavior);
}
host.Opened += new EventHandler(host_Opened);
host.Open();
Console.Read();
}
}
static void host_Opened(object sender, EventArgs e)
{
Console.WriteLine("计算机服务已经被打开了......");
}
}
}
客户端代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WcfLifeCycle.Client
{
/// <summary>
/// wcf实例模型
/// </summary>
public class ClientCalculator
{
public static void Main(string[] args)
{
CalculatorServiceClient proxy1 = new CalculatorServiceClient();
Console.WriteLine("proxy1 Call [Add] method");
proxy1.CalculatorAdd(12d, 13d);
Console.WriteLine("proxy1 Call [Add] method");
proxy1.CalculatorAdd(12d, 13d);
Console.WriteLine("proxy1 Call [Add] method");
proxy1.CalculatorAdd(12d, 13d);
Console.WriteLine("******************************************");
CalculatorServiceClient proxy2= new CalculatorServiceClient();
Console.WriteLine("proxy2 Call [Add] method");
proxy2.CalculatorAdd(12d, 13d);
Console.WriteLine("proxy2 Call [Add] method");
proxy2.CalculatorAdd(12d, 13d);
Console.WriteLine("proxy2 Call [Add] method");
proxy2.CalculatorAdd(12d, 13d);
Console.Read();
}
}
}
结果示例图
服务端:
客户端
小结:Percall模式,客户端每次发出请求后,服务端都会生成一个实例响应,响应完毕后自行销毁。
PerSession模式
PerSession模式工作的流程如下:
- 客户端创建代理对象(Proxy)
- 客户端第一次调用代理对象的一个契约操作,代理对象将其调用请求传递给服务宿主
- 宿主程序创建新的服务对象,并执行请求操作,如果有必要,返回客户端应答
- 客户端再次发出调用操作的请求(图中的proxy一与服务实例一对应,proxy二与服务实例二对应),宿主会先判断是否已有建立好的会话,如果存在,则不需要再创建新的服务对象,直接使用老对象即可,如果不存在则新建一个服务对象。
- 在时间达到指定要求或者因一些特殊原因,会话会过期,此时服务对象销毁.或者客户端关闭,服务对象也会自动销毁。
示例代码展示
服务契约代码如下:
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace Contract
{
[ServiceContract(Name="CalculatorService",Namespace="http://www/gxs.com")]
public interface ICalculator:IDisposable
{
[OperationContract(Name = "CalculatorAdd")]
double Add(double x, double y);
[OperationContract(Name = "CalculatorSubtract")]
double Subtract(double x, double y);
}
}
服务代码如下:
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Contract;
using System.ServiceModel;
namespace Service
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class CalculatorPerSession : ICalculator
{
public CalculatorPerSession()
{
Console.WriteLine("Create a new instance!!!");
}
public double Add(double x, double y)
{
Console.WriteLine("Call [Add] Method");
return x + y;
}
public double Subtract(double x, double y)
{
Console.WriteLine("Call [Subtract] Method");
return x - y;
}
public void Dispose()
{
Console.WriteLine("The instance has been disposed!!!");
}
}
}
服务端宿主代码
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using Contract;
using Service;
using System.ServiceModel.Description;
namespace Hosting
{
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(CalculatorPerSession)))
{
host.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "http://127.0.0.1:8089/CalculatorService");
//ServiceMetadataBehavior:控制服务元数据和相关信息的发布
if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
{
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true;
behavior.HttpGetUrl = new Uri("http://127.0.0.1:8089/CalculatorService/metadata");
host.Description.Behaviors.Add(behavior);
}
host.Opened += new EventHandler(host_Opened);
host.Open();
Console.Read();
}
}
static void host_Opened(object sender, EventArgs e)
{
Console.WriteLine("计算机服务已经被打开了......");
}
}
}
客户端代码如下:
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WcfLifeCycle.Client
{
/// <summary>
/// wcf实例模型
/// </summary>
public class ClientCalculator
{
public static void Main(string[] args)
{
CalculatorServiceClient proxy1 = new CalculatorServiceClient();
Console.WriteLine("proxy1 Call [Add] method");
proxy1.CalculatorAdd(12d, 13d);
Console.WriteLine("proxy1 Call [Add] method");
proxy1.CalculatorAdd(12d, 13d);
Console.WriteLine("proxy1 Call [Add] method");
proxy1.CalculatorAdd(12d, 13d);
Console.WriteLine("******************************************");
CalculatorServiceClient proxy2= new CalculatorServiceClient();
Console.WriteLine("proxy2 Call [Add] method");
proxy2.CalculatorAdd(12d, 13d);
Console.WriteLine("proxy2 Call [Add] method");
proxy2.CalculatorAdd(12d, 13d);
Console.WriteLine("proxy2 Call [Add] method");
proxy2.CalculatorAdd(12d, 13d);
Console.Read();
}
}
}
结果实例图
服务端
客户端
小结:PerSession模式,proxy与服务实例对应。不同的proxy有不同的服务实例。
Single模式
Single模式的工作流程如下:
- 服务端启动,同时创建服务对象
- 客户端通过代理调用契约操作
- 第一步中创建的服务对象接受请求 ,并执行操作,进行必要的应答
- 第一步创建的服务对象将一直保留
- 服务关闭,第一步创建的对象销毁
示例代码展示:
服务契约代码
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace Contract
{
[ServiceContract(Name="CalculatorService",Namespace="http://www/gxs.com")]
public interface ICalculator:IDisposable
{
[OperationContract(Name = "CalculatorAdd")]
double Add(double x, double y);
[OperationContract(Name = "CalculatorSubtract")]
double Subtract(double x, double y);
}
}
服务代码
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Contract;
using System.ServiceModel;
namespace Service
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class CalculatorPerSingle : ICalculator
{
public CalculatorPerSingle()
{
Console.WriteLine("Create a new instance!!!");
}
public double Add(double x, double y)
{
Console.WriteLine("Call [Add] Method");
return x + y;
}
public double Subtract(double x, double y)
{
Console.WriteLine("Call [Subtract] Method");
return x - y;
}
public void Dispose()
{
Console.WriteLine("The instance has been disposed!!!");
}
}
}
服务端宿主代码
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using Contract;
using Service;
using System.ServiceModel.Description;
namespace Hosting
{
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(CalculatorPerSingle), new Uri("http://127.0.0.1:8089")))
{
host.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "CalculatorServiceOne");
host.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "CalculatorServiceTwo");
//ServiceMetadataBehavior:控制服务元数据和相关信息的发布
if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
{
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true;
behavior.HttpGetUrl = new Uri("http://127.0.0.1:8089/CalculatorService/metadata");
host.Description.Behaviors.Add(behavior);
}
host.Opened += new EventHandler(host_Opened);
host.Open();
Console.Read();
}
}
static void host_Opened(object sender, EventArgs e)
{
Console.WriteLine("计算机服务已经被打开了......");
}
}
}
客户端一代码
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WcfLifeCycle.Client
{
/// <summary>
/// wcf实例模型
/// </summary>
public class ClientOneCalculator
{
public static void Main(string[] args)
{
CalculatorServiceClient proxy1 = new CalculatorServiceClient("WSHttpBinding_CalculatorService");
Console.WriteLine("I am EndPointOne");
Console.WriteLine("proxy1 Call [Add] method");
proxy1.CalculatorAdd(12d, 13d);
Console.WriteLine("proxy1 Call [Add] method");
proxy1.CalculatorAdd(12d, 13d);
Console.WriteLine("proxy1 Call [Add] method");
proxy1.CalculatorAdd(12d, 13d);
proxy1.Close();
Console.Read();
}
}
}
客户端一配置文件app.config
View Code
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_CalculatorService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
<binding name="WSHttpBinding_CalculatorService1" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://127.0.0.1:8089/CalculatorServiceOne"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_CalculatorService"
contract="Client.CalculatorService" name="WSHttpBinding_CalculatorService">
<identity>
<userPrincipalName value="guxiaoshi-PC\guxiaoshi" />
</identity>
</endpoint>
<endpoint address="http://127.0.0.1:8089/CalculatorServiceTwo"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_CalculatorService1"
contract="Client.CalculatorService" name="WSHttpBinding_CalculatorService1">
<identity>
<userPrincipalName value="guxiaoshi-PC\guxiaoshi" />
</identity>
</endpoint>
</client>
</system.serviceModel>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
客户端二代码
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ClientTwo.Client;
namespace ClientTwo
{
class ClientTwoCalculator
{
static void Main(string[] args)
{
CalculatorServiceClient proxy1 = new CalculatorServiceClient("WSHttpBinding_CalculatorService1");
Console.WriteLine("I am EndPointTwo");
Console.WriteLine("proxy1 Call [Add] method");
proxy1.CalculatorAdd(12d, 13d);
Console.WriteLine("proxy1 Call [Add] method");
proxy1.CalculatorAdd(12d, 13d);
Console.WriteLine("proxy1 Call [Add] method");
proxy1.CalculatorAdd(12d, 13d);
proxy1.Close();
Console.Read();
}
}
}
客户端二配置文件app.config
View Code
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_CalculatorService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
<binding name="WSHttpBinding_CalculatorService1" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://127.0.0.1:8089/CalculatorServiceOne"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_CalculatorService"
contract="Client.CalculatorService" name="WSHttpBinding_CalculatorService">
<identity>
<userPrincipalName value="guxiaoshi-PC\guxiaoshi" />
</identity>
</endpoint>
<endpoint address="http://127.0.0.1:8089/CalculatorServiceTwo"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_CalculatorService1"
contract="Client.CalculatorService" name="WSHttpBinding_CalculatorService1">
<identity>
<userPrincipalName value="guxiaoshi-PC\guxiaoshi" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
结果实例图
服务端
客户端一
客户端二
小结:服务端自始至终只有一个实例与客户端进行消息的交换。
总结:上文是对服务对象实例生命周期的介绍.Percall:新的请求产生新的服务对象与之交换信息,服务对象自行销毁;PerSession:相同的客户端由同一个服务对象与之交换信息(这里的客户端指proxy);Single:服务端只有一个服务实例,服务打开时服务实例就会被创建。