刚学WCF的时候没发现这个问题,调得很愉快却没有发现其实“暗藏杀机”,可谓危险重重,还好后来觉得是有些不妥,于是google“WCF需要关闭吗”,立马找到了几个博客园的链接,进去一看,各位大侠均纷纷表示“关是一定要关的,但是你还不能用完就关,因为关了,就不能再打开了,还得New,可以New的成本又有一点高”,好吧,这么说来就只有好好规划一下了。
那么什么时候该关呢,答案是异常了的时候,MSDN给出了代码(错误处理部分,找不到链接了,见谅)
catch (CommunicationException)
{
client.Abort();
}
catch (TimeoutException)
{
clent.Abort();
}
所以这样一来,就有了解决方案,那就是在每一处需要调用Wcf和代码里加上try catch,大概就成了如下这样
UserClient client = new UserClient();
try
{
client.Create(new User(){
UserName="xian",
Password="123"});
}
catch (CommunicationException)
{
client.Abort();
}
catch (TimeoutException)
{
clent.Abort();
}
这样挺好的,没错,不过我们总不可能只调用一个Wcf接口吧,我们要调的可是一系列,也就是若干接口,那么就我们就需要写很多重复的错误处理代码块了。这样原来没什么问题,而且是理所当然的事情。可是我记得某位老鸟说过当代码出现很大程度重复的时候你就该重构它了(也是在博客园看到了,很普通但深入人心的道理),并且为了让代码好看一点,我们还是想想好点的办法。
如果你有些经验,肯定马上就去想到使用委托来实现消除代码的冗余,是的,下面就是我这次的实现方式,代码如下:
ServiceInvokeHelper调用辅助类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace Helper
{
/// <summary>
/// Wcf服务调用辅助类
/// </summary
public static class ServiceInvokeHelper<TChannel> where TChannel : ICommunicationObject, new()
{
#region private fields
private static Dictionary<string, TChannel> _ChannelDic = new Dictionary<string, TChannel>();
private static object _Lockhelper = new object();
private static TResult TryFunc<TResult>(Func<TChannel, TResult> func, TChannel channel)
{
string tChannelName = typeof(TChannel).FullName;
try
{
return func(channel);
}
catch (CommunicationException)
{
channel.Abort();
lock (_Lockhelper)
_ChannelDic.Remove(tChannelName);
throw;
}
catch (TimeoutException)
{
channel.Abort();
lock (_Lockhelper)
_ChannelDic.Remove(tChannelName);
throw;
}
catch (Exception)
{
channel.Abort();
lock (_Lockhelper)
_ChannelDic.Remove(tChannelName);
throw;
}
}
private static TChannel GetChannel()
{
TChannel instance;
string tChannelName = typeof(TChannel).FullName;
if (!_ChannelDic.ContainsKey(tChannelName))
{
lock (_Lockhelper)
{
instance = Activator.CreateInstance<TChannel>();
_ChannelDic.Add(tChannelName, instance);
}
}
else
{
instance = _ChannelDic[tChannelName];
}
if (instance.State != CommunicationState.Opened && instance.State != CommunicationState.Opening)
instance.Open();
return instance;
}
/// <summary>
/// 直接调用,无返回值
/// </summary>
public static void Invoke(Action<TChannel> action)
{
TChannel instance = GetChannel();
TryFunc(
client =>
{
action(client);
return (object)null;
}
, instance);
}
/// <summary>
/// 有返回值的调用
/// </summary>
public static TResult Invoke<TResult>(Func<TChannel, TResult> func)
{
TChannel instance = GetChannel();
ICommunicationObject channel = instance as ICommunicationObject;
TResult returnValue = default(TResult);
returnValue = TryFunc(func, instance);
return returnValue;
}
}
}
有了以上代码,我们就可以这样调Wcf了
ServiceInvokeHelper<UserClient>.Invoke(client=>client.Create({new User{
UserName="xian";
Password="123";
}}));
测试过程中发现这样不支持out 和ref参数的调用,比如这样是不可以的
public void GetUserList(int pageindex,int pagesize,ref count)
{
return ServiceInvokeHelper<UserClient>.Invoke(client=>client.GetUserList(pageindex,pagesize,ref count));
}
但是我们可以变通成如下模样
public void GetUserList(int pageindex,int pagesize,ref count)
{
return ServiceInvokeHelper<UserClient>.Invoke(client=>
{
int tmpCount = -1;
client.GetUserList(pageindex,pagesize,ref tmpCount));
count = tmpCount;
}
}