WCF如何使用动态代理精简代码架构
- 作者: 3355748
- 来源: 51数据库
- 2021-08-26
使用castle.core.dll实现,核心代码是使用castle.dynamicproxy.proxygenerator类的createinterfaceproxywithouttarget方法动态创建代理对象
nuget上面castle.core的下载量1.78亿之多
一、重构前的项目代码
重构前的项目代码共7层代码,其中wcf服务端3层,wcf接口层1层,客户端3层,共7层
1.服务端wcf服务层suncreate.infoplatform.server.service
2.服务端数据库访问接口层suncreate.infoplatform.server.bussiness
3.服务端数据库访问实现层suncreate.infoplatform.server.bussiness.impl
4.wcf接口层suncreate.infoplatform.contract
5.客户端代理层suncreate.infoplatform.client.proxy
6.客户端业务接口层suncreate.infoplatform.client.bussiness
7.客户端业务实现层suncreate.infoplatform.client.bussiness.impl
二、客户端通过动态代理重构
1.实现在拦截器中添加ticket、处理异常、close对象
2.客户端不需要再写代理层代码,而使用动态代理层
3.对于简单的增删改查业务功能,也不需要再写业务接口层和业务实现层,直接调用动态代理;对于复杂的业务功能以及缓存,才需要写业务接口层和业务实现层
客户端动态代理工厂类proxyfactory代码(该代码目前写在客户端业务实现层):
using castle.dynamicproxy;
using system;
using system.collections.concurrent;
using system.collections.generic;
using system.linq;
using system.servicemodel;
using system.text;
using system.threading.tasks;
namespace suncreate.infoplatform.client.bussiness.imp
{
/// <summary>
/// wcf服务工厂
/// pf是proxyfactory的简写
/// </summary>
public class pf
{
/// <summary>
/// 拦截器缓存
/// </summary>
private static concurrentdictionary<type, iinterceptor> _interceptors = new concurrentdictionary<type, iinterceptor>();
/// <summary>
/// 代理对象缓存
/// </summary>
private static concurrentdictionary<type, object> _objs = new concurrentdictionary<type, object>();
private static proxygenerator _proxygenerator = new proxygenerator();
/// <summary>
/// 获取wcf服务
/// </summary>
/// <typeparam name="t">wcf接口</typeparam>
public static t get<t>()
{
type interfacetype = typeof(t);
iinterceptor interceptor = _interceptors.getoradd(interfacetype, type =>
{
string servicename = interfacetype.name.substring(1); //服务名称
channelfactory<t> channelfactory = new channelfactory<t>(servicename);
return new proxyinterceptor<t>(channelfactory);
});
return (t)_objs.getoradd(interfacetype, type => _proxygenerator.createinterfaceproxywithouttarget(interfacetype, interceptor)); //根据接口类型动态创建代理对象,接口没有实现类
}
}
}
客户端拦截器类proxyinterceptor<t>代码(该代码目前写在客户端业务实现层):
using castle.dynamicproxy;
using log4net;
using suncreate.common.base;
using suncreate.infoplatform.client.bussiness;
using system;
using system.collections.generic;
using system.linq;
using system.reflection;
using system.servicemodel;
using system.servicemodel.channels;
using system.text;
using system.threading.tasks;
namespace suncreate.infoplatform.client.bussiness.imp
{
/// <summary>
/// 拦截器
/// </summary>
/// <typeparam name="t">接口</typeparam>
public class proxyinterceptor<t> : iinterceptor
{
private static ilog _log = logmanager.getlogger(typeof(proxyinterceptor<t>));
private channelfactory<t> _channelfactory;
public proxyinterceptor(channelfactory<t> channelfactory)
{
_channelfactory = channelfactory;
}
/// <summary>
/// 拦截方法
/// </summary>
public void intercept(iinvocation invocation)
{
//准备参数
parameterinfo[] parameterinfoarr = invocation.method.getparameters();
object[] valarr = new object[parameterinfoarr.length];
for (int i = 0; i < parameterinfoarr.length; i++)
{
valarr[i] = invocation.getargumentvalue(i);
}
//执行方法
t server = _channelfactory.createchannel();
using (operationcontextscope scope = new operationcontextscope(server as icontextchannel))
{
try
{
hi.get<isecuritybussiness>().addticket();
invocation.returnvalue = invocation.method.invoke(server, valarr);
var value = hi.get<isecuritybussiness>().getvalue();
((ichannel)server).close();
}
catch (exception ex)
{
_log.error("proxyinterceptor " + typeof(t).name + " " + invocation.method.name + " 异常", ex);
((ichannel)server).abort();
}
}
//out和ref参数处理
for (int i = 0; i < parameterinfoarr.length; i++)
{
parameterinfo paraminfo = parameterinfoarr[i];
if (paraminfo.isout || paraminfo.parametertype.isbyref)
{
invocation.setargumentvalue(i, valarr[i]);
}
}
}
}
}
如何使用:
list<escorttask> list = pf.get<ibussdataservice>().getescorttasklist();
这里不用再写try catch,异常在拦截器中处理
三、wcf服务端通过动态代理,在拦截器中校验ticket、处理异常
服务端动态代理工厂类proxyfactory代码(代码中保存动态代理dll不是必需的):
using autofac;
using castle.dynamicproxy;
using castle.dynamicproxy.generators;
using suncreate.common.base;
using system;
using system.collections.concurrent;
using system.collections.generic;
using system.io;
using system.linq;
using system.reflection;
using system.servicemodel;
using system.servicemodel.activation;
using system.text;
using system.threading.tasks;
namespace suncreate.infoplatform.winservice
{
/// <summary>
/// 动态代理工厂
/// </summary>
public class proxyfactory
{
/// <summary>
/// 拦截器缓存
/// </summary>
private static concurrentdictionary<type, iinterceptor> _interceptors = new concurrentdictionary<type, iinterceptor>();
/// <summary>
/// 代理对象缓存
/// </summary>
private static concurrentdictionary<type, object> _objs = new concurrentdictionary<type, object>();
private static proxygenerator _proxygenerator;
private static modulescope _scope;
private static proxygenerationoptions _options;
static proxyfactory()
{
attributestoavoidreplicating.add(typeof(servicecontractattribute)); //动态代理类不继承接口的servicecontractattribute
string path = appdomain.currentdomain.basedirectory;
_scope = new modulescope(true, false,
modulescope.default_assembly_name,
path.combine(path, modulescope.default_file_name),
"mydynamicproxy.proxies",
path.combine(path, "mydymamicproxy.proxies.dll"));
var builder = new defaultproxybuilder(_scope);
_options = new proxygenerationoptions();
//给动态代理类添加aspnetcompatibilityrequirementsattribute属性
propertyinfo proinfoaspnet = typeof(aspnetcompatibilityrequirementsattribute).getproperty("requirementsmode");
customattributeinfo customattributeinfo = new customattributeinfo(typeof(aspnetcompatibilityrequirementsattribute).getconstructor(new type[0]), new object[0], new propertyinfo[] { proinfoaspnet }, new object[] { aspnetcompatibilityrequirementsmode.allowed });
_options.additionalattributes.add(customattributeinfo);
//给动态代理类添加servicebehaviorattribute属性
propertyinfo proinfoinstancecontextmode = typeof(servicebehaviorattribute).getproperty("instancecontextmode");
propertyinfo proinfoconcurrencymode = typeof(servicebehaviorattribute).getproperty("concurrencymode");
customattributeinfo = new customattributeinfo(typeof(servicebehaviorattribute).getconstructor(new type[0]), new object[0], new propertyinfo[] { proinfoinstancecontextmode, proinfoconcurrencymode }, new object[] { instancecontextmode.single, concurrencymode.multiple });
_options.additionalattributes.add(customattributeinfo);
_proxygenerator = new proxygenerator(builder);
}
/// <summary>
/// 动态创建代理
/// </summary>
public static object createproxy(type contractinterfacetype, type impinterfacetype)
{
iinterceptor interceptor = _interceptors.getoradd(impinterfacetype, type =>
{
object _impl = hi.provider.getservice(impinterfacetype);
return new proxyinterceptor(_impl);
});
return _objs.getoradd(contractinterfacetype, type => _proxygenerator.createinterfaceproxywithouttarget(contractinterfacetype, _options, interceptor)); //根据接口类型动态创建代理对象,接口没有实现类
}
/// <summary>
/// 保存动态代理dll
/// </summary>
public static void save()
{
string filepath = path.combine(_scope.weaknamedmoduledirectory, _scope.weaknamedmodulename);
if (file.exists(filepath))
{
file.delete(filepath);
}
_scope.saveassembly(false);
}
}
}
说明:object _impl = hi.provider.getservice(impinterfacetype); 这句代码用于创建数据库访问层对象,hi是项目中的一个工具类,类似autofac框架的功能
服务端拦截器类proxyinterceptor<t>代码:
using castle.dynamicproxy;
using log4net;
using suncreate.common.base;
using suncreate.infoplatform.server.bussiness;
using system;
using system.collections.generic;
using system.linq;
using system.reflection;
using system.servicemodel;
using system.servicemodel.channels;
using system.text;
using system.threading.tasks;
namespace suncreate.infoplatform.winservice
{
/// <summary>
/// 拦截器
/// </summary>
public class proxyinterceptor : iinterceptor
{
private static ilog _log = logmanager.getlogger(typeof(proxyinterceptor));
private object _impl;
public proxyinterceptor(object impl)
{
_impl = impl;
}
/// <summary>
/// 拦截方法
/// </summary>
public void intercept(iinvocation invocation)
{
//准备参数
parameterinfo[] parameterinfoarr = invocation.method.getparameters();
object[] valarr = new object[parameterinfoarr.length];
for (int i = 0; i < parameterinfoarr.length; i++)
{
valarr[i] = invocation.getargumentvalue(i);
}
//执行方法
try
{
if (hi.get<isecurityimp>().checkticket())
{
type impltype = _impl.gettype();
methodinfo methodinfo = impltype.getmethod(invocation.method.name);
invocation.returnvalue = methodinfo.invoke(_impl, valarr);
}
}
catch (exception ex)
{
_log.error("proxyinterceptor " + invocation.targettype.name + " " + invocation.method.name + " 异常", ex);
}
//out和ref参数处理
for (int i = 0; i < parameterinfoarr.length; i++)
{
parameterinfo paraminfo = parameterinfoarr[i];
if (paraminfo.isout || paraminfo.parametertype.isbyref)
{
invocation.setargumentvalue(i, valarr[i]);
}
}
}
}
}
服务端wcf的servicehost工厂类:
using spring.servicemodel.activation;
using system;
using system.collections.generic;
using system.linq;
using system.reflection;
using system.servicemodel;
using system.text;
using system.threading.tasks;
namespace suncreate.infoplatform.winservice
{
public class myservicehostfactory : servicehostfactory
{
public myservicehostfactory() { }
public override servicehostbase createservicehost(string reference, uri[] baseaddresses)
{
assembly contractassembly = assembly.getassembly(typeof(suncreate.infoplatform.contract.ibasedataservice));
assembly impassembly = assembly.getassembly(typeof(suncreate.infoplatform.server.bussiness.ibasedataimp));
type contractinterfacetype = contractassembly.gettype("suncreate.infoplatform.contract.i" + reference);
type impinterfacetype = impassembly.gettype("suncreate.infoplatform.server.bussiness.i" + reference.replace("service", "imp"));
if (contractinterfacetype != null && impinterfacetype != null)
{
var proxy = proxyfactory.createproxy(contractinterfacetype, impinterfacetype);
servicehostbase host = new servicehost(proxy, baseaddresses);
return host;
}
else
{
return null;
}
}
}
}
svc文件配置servicehost工厂类:
<%@ servicehost language="c#" debug="true" service="basedataservice" factory="suncreate.infoplatform.winservice.myservicehostfactory" %>
如何使用自定义的servicehost工厂类启动wcf服务,下面是部分代码:
myservicehostfactory factory = new myservicehostfactory();
list<servicehostbase> hostlist = new list<servicehostbase>();
foreach (var ofile in dirinfo.getfiles())
{
try
{
string strsername = ofile.name.replace(ofile.extension, "");
string strurl = string.format(m_strbaseurl, m_serverport, ofile.name);
var host = factory.createservicehost(strsername, new uri[] { new uri(strurl) });
if (host != null)
{
hostlist.add(host);
}
}
catch (exception ex)
{
console.writeline("出现异常:" + ex.message);
m_log.errorformat(ex.message + ex.stacktrace);
}
}
proxyfactory.save();
foreach (var host in hostlist)
{
try
{
foreach (var endpoint in host.description.endpoints)
{
endpoint.endpointbehaviors.add(new myendpointbehavior()); //用于添加消息拦截器、全局异常拦截器
}
host.open();
m_lshost.tryadd(host);
}
catch (exception ex)
{
console.writeline("出现异常:" + ex.message);
m_log.errorformat(ex.message + ex.stacktrace);
}
}
wcf服务端再也不用写service层了
四、当我需要添加一个wcf接口,以实现一个查询功能,比如查询所有组织机构,重构前,我需要在7层添加代码,然后客户端调用,重构后,我只需要在3层添加代码,然后客户端调用
1.在wcf接口层添加接口
2.在服务端数据访问接口层添加接口
3.在服务端数据访问实现层添加实现方法
4.客户端调用:var orglist = pf.get<ibasedataservice>().getorglist();
重构前,需要在7层添加代码,虽然每层代码都差不多,可以复制粘贴,但是复制粘贴也很麻烦啊,重构后省事多了,从此再也不怕写增删改查了
五、性能损失
主要是invocation.method.invoke比直接调用慢,耗时是直接调用的2、3倍,但是多花费的时间跟数据库查询耗时比起来,是微不足道的
以上就是wcf如何使用动态代理精简代码架构的详细内容,更多关于wcf使用动态代理精简代码架构的资料请关注其它相关文章!
