Learning WCF:Life Cycle of Service instance


示例代码下载地址:WCFDemo1Day

概述

客户端向WCF服务发出请求后,服务端会实例化一个Service对象(实现了契约接口的对象)用来处理请求,实例化Service对象以及维护其生命周期的方式在WCF中共有三种不同的类型,分别是:

  • Per-Call
  • Per-Session
  • Single

程序中通过设置ServiceBehavior特性来指定,如下:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class CommService : ICommContract
{
}

这是枚举InstanceContextMode的内容:

public enum InstanceContextMode
{
    PerSession = 0,
    PerCall = 1,
    Single = 2,
}

Per-Call

每次调用服务端服务端方法,服务端都会实例化一个对象来处理请求,为观察结果,现编写如下代码:

服务类:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class CommService : ICommContract
{
    public CommService()
    {
        Console.WriteLine("构造函数被执行");
    }

    public int Add(int a, int b)
    {
            Console.WriteLine("Add被调用,Thread:" + Thread.CurrentThread.ManagedThreadId + " fromPool:" + Thread.CurrentThread.IsThreadPoolThread);

        return a + b;
    }

    public void Dispose()
    {
        Console.WriteLine("对象销毁");
        Console.WriteLine("_____________________________");
    }

    public void SendStr(string str)
    {
        Console.WriteLine("SendStr被调用,Thread:" + Thread.CurrentThread.ManagedThreadId + " fromPool:" + Thread.CurrentThread.IsThreadPoolThread);
    }
}

如果服务类实现了接口IDisposable,当服务类被销毁时会回调Dispose方法,可以在Dispose方法中写一些WriteLine语句以观察对象的生命周期。

客户端代码:

class Program
{
    static void Main(string[] args)
    {
        CommService.CommContractClient client = new CommService.CommContractClient();

        client.SendStr("hello");
        client.SendStr("hello");
        client.SendStr("hello");

        client.Close();
    }
}

运行效果:
捕获.PNG-11.5kB

Per-Session

InstanceContextMode被设置为PerSession后,同一个客户端多次调用一个远程Web服务方法时,服务端只会实例化一次,修改上面的服务端代码第一行如下:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]

再看运行效果:
捕获.PNG-10.4kB

上面的实例代码所使用的绑定模式为WSHttpBinding,如果使用BasicHttpBinding的话,即使是指定了InstanceContextModePerSession服务端也不会保存会话,现将支持Per-Session的绑定方式列举如下:

  • WSXXXBinding(with Message security or reliability)
  • NetTcpXXXBinding
  • NetXXXPipeBinding

后两个容易理解,第一个括号里什么什么玩意儿,请看宿主代码:

 class Program
{
    static void Main(string[] args)
    {
        Uri baseURI = new Uri("http://localhost:8000/Services");
        ServiceHost host = new ServiceHost(typeof(CommService), baseURI);

        try
        {
            WSHttpBinding binding = new WSHttpBinding();
            binding.Security.Mode = SecurityMode.None;
            host.AddServiceEndpoint(typeof(ICommContract), binding, "CommonService");

            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
            smb.HttpGetUrl = new Uri("http://localhost:8080/Services");
            smb.HttpGetEnabled = true;
            host.Description.Behaviors.Add(smb);

            host.Open();
            Console.WriteLine("The service is ready.");
            Console.WriteLine("Press <ENTER> to terminate service.");
            Console.WriteLine();
            Console.ReadLine();

            host.Close();
        }
        catch (CommunicationException ce)
        {
            Console.WriteLine("An exception occurred: {0}", ce.Message);
            host.Abort();
        }
    }
}

binding.Security.Mode默认为Message,现在把它改成None再运行程序:

捕获.PNG-11.5kB

哈,虽然InstanceContextMode设置为了Per-Session,实际上还是Per-Call,这部分内容和安全性有关,希望我有时间以后会写到吧,今天暂时不去研究。

除了绑定方式,还有一个地方也影响到了Per-Session是否起作用,它是契约接口特性ServiceContract的属性SessionMode,该枚举内容如下:

public enum SessionMode
{
    Allowed = 0,
    Required = 1,
    NotAllowed = 2,
}

SessionMode属性默认是Allowed,如果设置为Required表示必须启用会话模式,NotAllowed表示必须不能启动会话模式,Allowed表示无所谓。谁有兴趣可以一一尝试。现将契约接口的特性改成如下:

[ServiceContract(Namespace = "zzy0471.cnblogs.com.CommService", SessionMode = SessionMode.NotAllowed)]
public interface ICommContract : IDisposable
{
    [OperationContract()]
    int Add(int a, int b);

     [OperationContract()]
    void SendStr(String str);
}

再运行程序:

捕获.PNG-12.1kB

虽然InstanceContextMode设置为了Per-Session,实际上还是Per-Call

此外,还有一个需要注意的地方,如果在方法契约特性中设置属性IsTerminatingtrue,如下图

[OperationContract(IsTerminating = true)]
void SendStr(String str);

运行服务端会导致运行时错误,IsTerminating设置为了truePer-Session模式相矛盾,IsTerminating的意思是:“获取或设置一个值,该值指示服务操作在发送答复消息(如果存在)后,是否会导致服务器关闭会话”

Per-Single

InstanceContextMode被设置为PerSingle后,所有的客户端请求都只有一个服务端实例对象来处理,服务启动时对象创建,服务关闭时对象销毁。现修改InstanceContextMode特性如下:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class CommService : ICommContract
{
    public CommService()
    {
        Console.WriteLine("构造函数被执行");
    }

    public int Add(int a, int b)
    {
        Console.WriteLine("Add被调用,Thread:" + Thread.CurrentThread.ManagedThreadId + " fromPool:" + Thread.CurrentThread.IsThreadPoolThread);

        return a + b;
    }

    public void Dispose()
    {
        Console.WriteLine("对象销毁");
        Console.WriteLine("_____________________________");
    }

    public void SendStr(string str)
    {
        Console.WriteLine("SendStr被调用,Thread:" + Thread.CurrentThread.ManagedThreadId + " fromPool:" + Thread.CurrentThread.IsThreadPoolThread);
    }
}

为了方便观察,修改客户端代码,以模拟多个客户端:

class Program
{
    static void Main(string[] args)
    {
        for (int i = 0; i < 3; i++)
        {
            CommService.CommContractClient client = new CommService.CommContractClient();

            client.SendStr("hello");
            client.SendStr("hello");
            client.SendStr("hello");

            client.Close();
        }
    }
}

运行程序:
捕获.PNG-14.5kB

posted @ 2017-06-02 08:19 会长 阅读(...) 评论(...) 编辑 收藏