原文地址:http://www.rainsts.net/article.asp?id=456

虽然通过设置 ServiceBehavior.InstanceContextMode 特性或关闭客户端代理可以达到释放服务对象的目的,但某些时候我们可能希望获得更好的主动权。

1. ReleaseServiceInstance

这应该是最直接的方式了。看下面例子的输出结果,我们可以看到在客户端代理对象释放之前,服务实例就被释放了。

[ServiceContract(SessionMode = SessionMode.Required)]
public interface IMyService
{
  [OperationContract]
  void Test();
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class MyServie : IMyService, IDisposable
{
  public MyServie()
  {
    Console.WriteLine("Constructor");
  }

  [OperationBehavior]
  public void Test()
  {
    OperationContext.Current.InstanceContext.ReleaseServiceInstance();
  }

  public void Dispose()
  {
    Console.WriteLine("Dispose");
  }
}

public class WcfTest
{
  public static void Test()
  {
    AppDomain.CreateDomain("Server").DoCallBack(delegate
    {
      ServiceHost host = new ServiceHost(typeof(MyServie), new Uri("http://localhost:8080/MyService"));
      host.AddServiceEndpoint(typeof(IMyService), new WSHttpBinding(), "");
      host.Open();
    });

    //-----------------------

    IMyService channel = ChannelFactory<IMyService>.CreateChannel(new WSHttpBinding(),
      new EndpointAddress("http://localhost:8080/MyService"));
      
    using (channel as IDisposable)
    {
      channel.Test();
      Thread.Sleep(2000);
      Console.WriteLine("Dispose Client Proxy...");
    }
  }
}


输出:
Constructor
Dispose
Dispose Client Proxy...

2. ReleaseInstanceMode

通过设置 OperationBehavior.ReleaseInstanceMode 也能达到控制服务对象释放的目的,一种有四种方式可供选择。

None : 缺省参数,表示不做任何处理。BeforeCall : 如果在方法调用开始前,Session 中已经有服务实例,则先释放该实例,然后创建新实例完成方法调用。AfterCall : 调用完方法后,释放当前服务实例。BeforeAndAfterCall : 调用前释放 Session 中的已有服务实例,然后创建新实例完成调用,结束调用后释放新实例。

测试原型

[ServiceContract(SessionMode = SessionMode.Required)]
public interface IMyService
{
  [OperationContract]
  void Test();

  [OperationContract]
  void TestBeforeCall();
  
  [OperationContract]
  void TestAfterCall();
  
  [OperationContract]
  void TestBeforeAndAfterCall();
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class MyServie : IMyService, IDisposable
{
  public MyServie()
  {
    Console.WriteLine("Constructor");
  }

  [OperationBehavior]
  public void Test()
  {
  }

  [OperationBehavior(ReleaseInstanceMode=ReleaseInstanceMode.BeforeCall)]
  public void TestBeforeCall()
  {
    Console.WriteLine("TestBeforeCall");
  }

  [OperationBehavior(ReleaseInstanceMode = ReleaseInstanceMode.AfterCall)]
  public void TestAfterCall()
  {
    Console.WriteLine("TestAfterCall");
  }

  [OperationBehavior(ReleaseInstanceMode = ReleaseInstanceMode.BeforeAndAfterCall)]
  public void TestBeforeAndAfterCall()
  {
    Console.WriteLine("TestBeforeAndAfterCall");
  }

  public void Dispose()
  {
    Console.WriteLine("Dispose");
  }
}

public class WcfTest
{
  public static void StartServer()
  {
    AppDomain.CreateDomain("Server").DoCallBack(delegate
    {
      ServiceHost host = new ServiceHost(typeof(MyServie), new Uri("http://localhost:8080/MyService"));
      host.AddServiceEndpoint(typeof(IMyService), new WSHttpBinding(), "");
      host.Open();
    });
  }
}


测试1 : TestBeforeCall

IMyService channel = ChannelFactory<IMyService>.CreateChannel(new WSHttpBinding(),
  new EndpointAddress("http://localhost:8080/MyService"));

using (channel as IDisposable)
{
  // 为 Session 创建一个服务实例。
  channel.Test();

  // 测试在调用方法前,释放前面创建的实例。
  channel.TestBeforeCall();

  Thread.Sleep(2000);
  Console.WriteLine("Dispose Client Proxy...");
}


输出:
Constructor
Dispose
Constructor
TestBeforeCall
Dispose Client Proxy...
Dispose

测试结果表明,第一个实例在调用前被正确释放。

测试2 : TestAfterCall

IMyService channel = ChannelFactory<IMyService>.CreateChannel(new WSHttpBinding(),
  new EndpointAddress("http://localhost:8080/MyService"));

using (channel as IDisposable)
{
  // 测试在方法调用完成后释放服务实例。
  channel.TestAfterCall();

  Thread.Sleep(2000);
  Console.WriteLine("Dispose Client Proxy...");
}


输出:
Constructor
TestAfterCall
Dispose
Dispose Client Proxy...

测试结果表明,在方法调用结束之后,客户端代理释放之前,服务实例被正确释放。

测试3 : TestBeforeAndAfterCall

IMyService channel = ChannelFactory<IMyService>.CreateChannel(new WSHttpBinding(),
  new EndpointAddress("http://localhost:8080/MyService"));

using (channel as IDisposable)
{
  // 为 Session 创建一个服务实例。
  channel.Test();

  // 测试在调用方法前,释放前面创建的服务实例,在方法调用完成后释放新创建的服务实例。
  channel.TestBeforeAndAfterCall();

  Thread.Sleep(2000);
  Console.WriteLine("Dispose Client Proxy...");
}


输出:
Constructor
Dispose
Constructor
TestBeforeAndAfterCall
Dispose
Dispose Client Proxy...

测试结果表明,第一个服务实例在方法调用前被正确释放,新创建的服务实例在方法调用结束后,客户端代理释放前也被正确释放。

我们可以使用 BeforeCall 来标注 Open 这样的方法,这样我们就可以使用一个新的对象来完成初始化操作,而不是去操作乱七八糟的已有状态;而 AfterCall 则可用于 Close / Dispose 等操作;至于 BeforeAndAfterCall 似乎更像是在模拟一个静态方法。

3. IsTerminating

使用 OperationContract.IsTerminating 也可以释放服务实例。

[ServiceContract(SessionMode = SessionMode.Required)]
public interface IMyService
{
  [OperationContract(IsTerminating=true)]
  void Test();
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class MyServie : IMyService, IDisposable
{
  public MyServie()
  {
    Console.WriteLine("Constructor");
  }

  [OperationBehavior]
  public void Test()
  {
    Console.WriteLine("Test");
  }

  public void Dispose()
  {
    Console.WriteLine("Dispose");
  }
}

public class WcfTest
{
  public static void Test()
  {
    AppDomain.CreateDomain("Server").DoCallBack(delegate
    {
      ServiceHost host = new ServiceHost(typeof(MyServie), new Uri("http://localhost:8080/MyService"));
      host.AddServiceEndpoint(typeof(IMyService), new WSHttpBinding(), "");
      host.Open();
    });

    //-----------------------

    IMyService channel = ChannelFactory<IMyService>.CreateChannel(new WSHttpBinding(),
      new EndpointAddress("http://localhost:8080/MyService"));

    using (channel as IDisposable)
    {
      channel.Test();

      Thread.Sleep(2000);
      Console.WriteLine("Client Proxy Dispose...");
    }
  }
}


输出:
Constructor
Test
Dispose
Client Proxy Dispose...

使用 IsTerminating 有那么点麻烦,一旦调用了标注 "IsTerminating=true" 的方法后,再次通过代理调用任何服务方法都不会自动创建新的服务实例。有关 "IsTerminating" 和 "IsInitiating" 的详细使用方法,另作文章说明。

IMyService channel = ChannelFactory<IMyService>.CreateChannel(new WSHttpBinding(),
  new EndpointAddress("http://localhost:8080/MyService"));

using (channel as IDisposable)
{
  channel.Test();
  channel.Test();

  Thread.Sleep(2000);
  Console.WriteLine("Client Proxy Dispose...");
}


抛出异常

未处理 System.InvalidOperationException
  Message="This channel cannot send any more messages because IsTerminating operation 'Test' has already been called."
posted on 2011-11-08 14:20  xiaoyaozhe  阅读(593)  评论(0编辑  收藏  举报