原文地址:http://www.rainsts.net/article.asp?id=456
虽然通过设置 ServiceBehavior.InstanceContextMode 特性或关闭客户端代理可以达到释放服务对象的目的,但某些时候我们可能希望获得更好的主动权。
1. ReleaseServiceInstance
这应该是最直接的方式了。看下面例子的输出结果,我们可以看到在客户端代理对象释放之前,服务实例就被释放了。
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 中的已有服务实例,然后创建新实例完成调用,结束调用后释放新实例。
测试原型
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
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
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
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 也可以释放服务实例。
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" 的详细使用方法,另作文章说明。
new EndpointAddress("http://localhost:8080/MyService"));
using (channel as IDisposable)
{
channel.Test();
channel.Test();
Thread.Sleep(2000);
Console.WriteLine("Client Proxy Dispose...");
}
抛出异常
Message="This channel cannot send any more messages because IsTerminating operation 'Test' has already been called."