欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

WCF如何使用动态代理精简代码架构

程序员文章站 2022-03-30 11:09:13
使用castle.core.dll实现,核心代码是使用castle.dynamicproxy.proxygenerator类的createinterfaceproxywithouttarget方法动态...

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