WCF如何使用动态代理精简代码架构
使用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使用动态代理精简代码架构的资料请关注其它相关文章!