.NET的动态编译与WS服务调用详解

动态编译与WS服务,有关系么?今天就乱弹一番,如何使用动态编译动态生成WS服务调用的代理类,然后通过这个代理类调用WS服务。
    首先,动态编译这玩意在.NET里面是非常简单的,实际上只涉及到两个类型:CodeDomProvider以及CompilerParameters他们都位于System.CodeDom.Compiler命名空间。
    以下代码可将源码动态编译为一个程序集:
动态编译


代码如下:

CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters codeParameters = new CompilerParameters();
codeParameters.GenerateExecutable = false; //编译为dll,如果为true则编译为exe
codeParameters.GenerateInMemory = true; //编译后的程序集保存到内存中
StringBuilder code = new StringBuilder();
//此处构造源代码
CompilerResults results = provider.CompileAssemblyFromSource(codeParameters, code.ToString());
Assembly assembly = null; //动态编译生成的程序集
if (!results.Errors.HasErrors)
{
    assembly = results.CompiledAssembly;
}

获得assembly后,随后我们即可以通过反射获取程序集里面的类型,然后实例化,调用类型方法…
    不过在此之前,我们得构造WS服务的代理类,它是什么样子的呢?我们使用WCF框架,创建服务代理类也是十分简单的,常见的代理类结构如下:
服务调用代理类


代码如下:

[ServiceContract(Namespace="http://www.jb51.net/")]
public interface TestService
{
    [OperationContract(Action = "http://www.jb51.net/HelloWorld", ReplyAction = "http://www.jb51.net/HelloWorldResponse")]
    string HelloWorld();
}
public class TestServiceClient : ClientBase<TestService>, TestService
{
    public TestServiceClient(Binding binding, EndpointAddress address) :
        base(binding, address)
    {
    }
    public string HelloWorld()
    {
        return base.Channel.HelloWorld();
    }
}

所以,我们要动态构造出代理类源码,应该知道服务的命名空间、服务方法的Action地址、ReplyAction地址,当然还有服务方法的名称,返回类型,参数列表。这里,我们省略掉服务方法的参数列表,构造代理类,实际上就是一个字符串组装的问题,先创建一个类型,用于保存构造代理类所要用到的参数:

服务代理类构造参数


代码如下:

public class WebServiceParamaters
{
    public string address;
    public string Address
    {
        get { return address; }
        set
        {
            address = value;
        }
    }
    private string serviceNamespace;
    public string ServiceNamespace
    {
        get { return serviceNamespace; }
        set
        {
            serviceNamespace = value;
        }
    }
   private string methodAction;
    public string MethodAction
    {
        get { return methodAction; }
        set
        {
            methodAction = value;
        }
    }
    private string methodReplyAction;
    public string MethodReplyAction
    {
        get { return methodReplyAction; }
        set
        {
            methodReplyAction = value;
        }
    }
    private string methodName;
    public string MethodName
    {
        get { return methodName; }
        set
        {
            methodName = value;
        }
    }
    private string returnType;
    public string ReturnType
    {
        get { return returnType; }
        set
        {
            returnType = value;
        }
    }
}

好,现在我们只需要构造出代理类源码,然后动态编译出代理类的程序集,最后通过反射调用服务方法:
WebServiceProxyCreator


代码如下:

public class WebServiceProxyCreator
{
    public Object WebServiceCaller(WebServiceParamaters parameters)
    {
        CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
        CompilerParameters codeParameters = new CompilerParameters();
        codeParameters.GenerateExecutable = false;
        codeParameters.GenerateInMemory = true;
        StringBuilder code = new StringBuilder();
        CreateProxyCode(code, parameters);
codeParameters.ReferencedAssemblies.Add("System.dll");
codeParameters.ReferencedAssemblies.Add("System.ServiceModel.dll");
        CompilerResults results = provider.CompileAssemblyFromSource(codeParameters, code.ToString());
        Assembly assembly = null;
        if (!results.Errors.HasErrors)
        {
            assembly = results.CompiledAssembly;
        }
        Type clientType = assembly.GetType("RuntimeServiceClient");
       ConstructorInfo ci = clientType.GetConstructor(new Type[] { typeof(Binding), typeof(EndpointAddress) });
        BasicHttpBinding binding = new BasicHttpBinding(); //只演示传统的WebService调用
        EndpointAddress address = new EndpointAddress(parameters.address);
        Object client = ci.Invoke(new object[] { binding, address });
        MethodInfo mi = clientType.GetMethod(parameters.MethodName);
        Object result = mi.Invoke(client, null);
        mi = clientType.GetMethod("Close"); //关闭代理
        mi.Invoke(client, null);
        return result;
   }
    public static void CreateProxyCode(StringBuilder code, WebServiceParamaters parameters)
    {
        code.AppendLine("using System;");
        code.AppendLine("using System.ServiceModel;");
        code.AppendLine("using System.ServiceModel.Channels;");
        code.Append(@"[ServiceContract(");
        if (!String.IsNullOrEmpty(parameters.ServiceNamespace))
        {
            code.Append("Namespace=\"").Append(parameters.ServiceNamespace).Append("\"");
        }
        code.AppendLine(")]");
        code.AppendLine("public interface IRuntimeService");
        code.AppendLine("{");
        code.Append("[OperationContract(");
        if (!String.IsNullOrEmpty(parameters.MethodAction))
        {
            code.Append("Action=\"").Append(parameters.MethodAction).Append("\"");
            if (!String.IsNullOrEmpty(parameters.MethodReplyAction))
            {
                code.Append(", ");
            }
        }
        if (!String.IsNullOrEmpty(parameters.MethodReplyAction))
        {
            code.Append("ReplyAction=\"").Append(parameters.MethodReplyAction).Append("\"");
        }
        code.AppendLine(")]");
        code.Append(parameters.ReturnType).Append(" ");
        code.Append(parameters.MethodName).AppendLine("();");
        code.AppendLine("}");
        code.AppendLine();
        code.AppendLine("public class RuntimeServiceClient : ClientBase<IRuntimeService>, IRuntimeService");
        code.AppendLine("{");
        code.AppendLine("public RuntimeServiceClient(Binding binding, EndpointAddress address) :base(binding, address)");
        code.AppendLine("{");
        code.AppendLine("}");
        code.Append("public ").Append(parameters.ReturnType).Append(" ");
        code.Append(parameters.MethodName).AppendLine("()");
        code.AppendLine("{");
        code.Append("return base.Channel.").Append(parameters.MethodName).AppendLine("();");
        code.AppendLine("}");
        code.AppendLine("}");
    }
}

注意,红色部分,由于代理类使用了WCF框架,所以编译时我们需要添加System.ServiceModel的引用,当然System.dll肯定是必须的,这里要注意,System.ServiceModel.dll应该保存到应用程序目录,否则动态编译时会引发异常,很简单,在工程引用中添加System.ServiceModel的引用,然后在属性中将拷贝到本地属性设置为true。
   到此,我们就可以直接通过传入的服务地址、服务方法名称以及相关的命名空间,即可调用服务(尽管我们只能调用无参服务,并且尽管我们也只能调用使用BasicHttpBinding绑定的服务,这些限制的原因是…我懒,好吧,相信只要经过一点改动即可去掉这些限制)。
   可惜,我们的程序还很傻:每次调用服务都需要去生成代码、编译、创建代理实例最后再调用,嗯…那就缓存吧:
  在WebServiceParameters类中重写GetHashCode方法:


代码如下:

public override int GetHashCode()
  {
      return String.Concat(serviceNamespace, methodAction, methodReplyAction, methodName, returnType).GetHashCode();
  }

然后在WebServiceProxyCreator中加入缓存机制:


代码如下:

public class WebServiceProxyCreator
   {
       private static Dictionary<int, Type> proxyTypeCatch = new Dictionary<int, Type>();

public Object WebServiceCaller(WebServiceParamaters parameters)
       {
           int key = parameters.GetHashCode();
           Type clientType = null;
           if (proxyTypeCatch.ContainsKey(key))
          {
              clientType = proxyTypeCatch[key];
              Debug.WriteLine("使用缓存");
          }
          else
          {

CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
              CompilerParameters codeParameters = new CompilerParameters();
              codeParameters.GenerateExecutable = false;
              codeParameters.GenerateInMemory = true;

StringBuilder code = new StringBuilder();
              CreateProxyCode(code, parameters);

codeParameters.ReferencedAssemblies.Add("System.dll");
              codeParameters.ReferencedAssemblies.Add("System.ServiceModel.dll");

CompilerResults results = provider.CompileAssemblyFromSource(codeParameters, code.ToString());
              Assembly assembly = null;
              if (!results.Errors.HasErrors)
              {
                  assembly = results.CompiledAssembly;
              }

clientType = assembly.GetType("RuntimeServiceClient");

proxyTypeCatch.Add(key, clientType);
          }
          ConstructorInfo ci = clientType.GetConstructor(new Type[] { typeof(Binding), typeof(EndpointAddress) });
          BasicHttpBinding binding = new BasicHttpBinding(); //只演示传统的WebService调用
          EndpointAddress address = new EndpointAddress(parameters.address);
          Object client = ci.Invoke(new object[] { binding, address });

MethodInfo mi = clientType.GetMethod(parameters.MethodName);
          Object result = mi.Invoke(client, null);
          mi = clientType.GetMethod("Close"); //关闭代理
          mi.Invoke(client, null);
          return result;
      }

}

(0)

相关推荐

  • 使用 C# 动态编译代码和执行的代码

    复制代码 代码如下: /* * 使用 C# 动态编译代码和执行 * 作者: yaob */ static void Main(string[] args) { // 编译器 CodeDomProvider cdp = CodeDomProvider.CreateProvider("C#"); // 编译器的参数 CompilerParameters cp = new CompilerParameters(); cp.ReferencedAssemblies.Add("Syst

  • c#动态编译执行对象方法示例 运用映射机制创建对象

    C#是一种编译型的语言,程序执行,首先要经过编译器编译,如何让C#像一种脚本一样,在要执行的时候,进行编译,这里,我们可以用Microsoft.CSharp空间下的CSharpCodeProvider提供类,来达到动态编译的效果.在这里,我新建一个控制台程序,在Program.cs类里引用using System.CodeDom.Compiler;using System.Reflection;using Microsoft.CSharp;三大命名空间 复制代码 代码如下: #region us

  • C# 动态编译、动态执行、动态调试

    在此基础上我做了一些封装,为使调用更加简单,并增加了对动态代码调试的支持,相同代码只编译一次的支持,代码改动自动重新编译,代码引用文件的自动加载和手工加载等功能. 如上图,我封装的类CSharpProvider很简单,下面说明一下一些公共成员的用法. 公共属性 AssemblyFileName:这个属性指定动态编译后生成的配件名称. CompilerParameters:这个属性指定编译的参数 References:这个属性指定被编译代码中的引用.调用者只要调用References.Add("x

  • 详细介绍.NET中的动态编译技术

    代码的动态编译并执行是一个.NET平台提供给我们的很强大的工具用以灵活扩展(当然是面对内部开发人员)复杂而无法估算的逻辑,并通过一些额外的代码来扩展我们已有 的应用程序.这在很大程度上给我们提供了另外一种扩展的方式(当然这并不能算是严格意义上的扩展,但至少为我们提供了一种思路). 动态代码执行可以应用在诸如模板生成,外加逻辑扩展等一些场合.一个简单的例子,为了网站那的响应速度,HTML静态页面往往是我们最好的选择,但基于数据驱动的网站往往又很难用静态页面实现,那么将动态页面生成html的工作或许

  • .NET 动态编译

    这在很大程度上给我们提供了另外一种扩展的方式(当然这并不能算是严格意义上的扩展,但至少为我们提供了一种思路). 动态代码执行可以应用在诸如模板生成,外加逻辑扩展等一些场合.一个简单的例子,为了网站那的响应速度,HTML静态页面往往是我们最好的选择,但基于数据驱动的网站往往又很难用静态页面实现,那么将动态页面生成html的工作或许就是一个很好的应用场合.另外,对于一些模板的套用,我们同样可以用它来做.另外这本身也是插件编写的方式. 最基本的动态编译 .Net为我们提供了很强大的支持来实现这一切我们

  • .NET的动态编译与WS服务调用详解

    动态编译与WS服务,有关系么?今天就乱弹一番,如何使用动态编译动态生成WS服务调用的代理类,然后通过这个代理类调用WS服务.    首先,动态编译这玩意在.NET里面是非常简单的,实际上只涉及到两个类型:CodeDomProvider以及CompilerParameters他们都位于System.CodeDom.Compiler命名空间.    以下代码可将源码动态编译为一个程序集:动态编译 复制代码 代码如下: CodeDomProvider provider = CodeDomProvide

  • SpringBoot动态Feign服务调用详解

    目录 1.Feign传统方式的不足 2.动态Feign 2.1.服务生产者 2.2.动态Feign 2.3.服务消费者 3.总结 1.Feign传统方式的不足 ①.在微服务架构中,当我们使用Feign传统方式进行服务调用的时候,需要在每个服务消费者中添加FeignClient接口,编写对应的方法,而且当服务生产者Handler新增方法之后,服务消费者也要在FeignClient接口中添加方法,这样的话,会有些累赘. 那么能不能在调用服务提供者方法的时候,传入生产者服务名称的动态生成FeignCl

  • SpringCloud服务的发现与调用详解

    目录 前言 一.服务提供者 二.服务消费者 总结 相关推荐 上一章:Eureka注册中心 前言 上一章中,我们介绍了Eureka注册中心及集群的搭建,这一节将介绍服务的发现和调用.注意,这个时候我们只有注册中心,并没有引入其他的组件,所以需要使用SpringCloud原生态的服务发现和调用的方式实现,循序渐进的带你走入微服务的世界. 上篇文章我们已经创建好了注册中心,这次我们需要创建一个服务提供者(provider)和一个服务消费者(consumer)两个项目. 一.服务提供者 新建Maven项

  • Java动态代理(设计模式)代码详解

    基础:需要具备面向对象设计思想,多态的思想,反射的思想: Java动态代理机制的出现,使得Java开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架.通过阅读本文,读者将会对Java动态代理机制有更加深入的理解.本文首先从Java动态代理的运行机制和特点出发,对其代码进行了分析,推演了动态生成类的内部实现. 代理模

  • Android启动内置APK和动态发送接收自定义广播实例详解

    Android启动内置APK和动态发送接收自定义广播实例详解 工作中遇到这样一个需求,需要为按键添加一个亲情号,提供一个接口启动内置的APK,思考再三决定更改Framework,利用广播机制去实现. 一.代码动态自主启动内置APK 我们都知道Android系统为我们提供了很多服务管理类,PackageManager主要是管理应用程序包,通过它就可以获取应用程序信息并构建Intent,启动对应的应用.除此之外Android还未我们提供了一些对应的类来管理相关的xml文件,比如说可以通过Packag

  • Java如何对方法进行调用详解

    一.方法调用 方法调用的唯一目的:确定要调用哪一个方法 方法调用分为解析调用和分派调用 二.非虚方法与虚方法 非虚方法: 静态方法,私有方法,父类中的方法,被final修饰的方法,实例构造器 与之对应不是非虚方法的就是虚方法了 它们都没有重写出其他版本的方法,非常适合在类加载阶段就进行解析(符号引用->直接引用) 三.调用指令 普通调用指令 invokestatic:调用静态方法 invokespecial:调用私有方法,父类中的方法,实例构造器方法,final方法 invokeinterfac

  • Java Spring Boot消息服务万字详解分析

    目录 消息服务概述 为什么要使用消息服务 异步处理 应用解耦 流量削峰 分布式事务管理 常用消息中间件介绍 ActiveMQ RabbitMQ RocketMQ RabbitMQ消息中间件 RabbitMQ简介 RabbitMQ工作模式介绍 Work queues(工作队列模式) Public/Subscribe(发布订阅模式) Routing(路由模式) Topics(通配符模式) RPC Headers RabbitMQ安装以及整合环境搭建 安装RabbitMQ 下载RabbitMQ 安装R

  • Javaweb动态开发最重要的Servlet详解

    目录 一.导入方式 二.Servlet生命周期 三.继承HttpServlet GET&POST 四.Servlet相关性质(八股文) 五.Request&Response 1.HttpServletRequest 2.HttpServletResponse 六.请求转发模型 七.请求重定向 一.导入方式 由于jdk中没有servlet对应的jar包,所以需要咱们手动引入,有两种方式: 1.可以采取向lib目录导入servlet-api的jar包的方式 2.在maven项目中设置如下坐标,

  • vue动态代理无须重启项目解决方案详解

    目录 1.背景 2.技术方案 2.1 旧的配置方案 2.2 新的配置方案思路 3. 配置 1.背景 当我们vue构建项目的时候,都会在vue.config.js中配置我们需要代理的服务器地址.有时候我们需要使用不同后端服务器地址,也就是我们开发中所说的测试环境.灰度环境.正式环境等,这个时候如果我们需要使用不同的环境地址的时候,就需要使用命令或者手动修改vue.config.js中配置来重新启动项目.当项目项目越来越大的时候,我们需要很长的时间来启动项目,如此反复,极大影响我们开发进度.这个时候

  • 使用 Node.js 实现图片的动态裁切及算法实例代码详解

    背景&概览 目前常见的图床服务都会有图片动态裁切的功能,主要的应用场景用以为各种终端和业务形态输出合适尺寸的图片. 一张动辄以 MB 为计量单位的原始大图,通常不会只设置一下显示尺寸就直接输出到终端中,因为体积太大加载体验会很差,除了影响加载速度还会增加终端设备的内存占用.所以要想在各种终端下都能保证图片质量的同时又确保输出合适的尺寸,那么此时就需要根据图片 URL 来对原始图片进行裁切,然后动态生成并输出一张新的图片. URL 的设计 图片 URL 需要包含图片 id.尺寸.质量等信息.有两种

随机推荐