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


        动态编译与WS服务,有关系么?今天就乱弹一番,如何使用动态编译动态生成WS服务调用的代理类,然后通过这个代理类调用WS服务。

        首先,动态编译这玩意在.NET里面是非常简单的,实际上只涉及到两个类型:CodeDomProvider以及CompilerParameters他们都位于System.CodeDom.Compiler命名空间。

        以下代码可将源码动态编译为一个程序集:

    隐藏行号 复制代码 动态编译
    1. CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
      
    2. CompilerParameters codeParameters = new CompilerParameters();
      
    3. codeParameters.GenerateExecutable = false; //编译为dll,如果为true则编译为exe
      
    4. codeParameters.GenerateInMemory = true; //编译后的程序集保存到内存中
      
    5. StringBuilder code = new StringBuilder();
      
    6. //此处构造源代码
      
    7. CompilerResults results = provider.CompileAssemblyFromSource(codeParameters, code.ToString());
      
    8. Assembly assembly = null; //动态编译生成的程序集
      
    9. if (!results.Errors.HasErrors)
      
    10. {
      
    11.     assembly = results.CompiledAssembly;
      
    12. }
      
    13. 
      

        获得assembly后,随后我们即可以通过反射获取程序集里面的类型,然后实例化,调用类型方法…

        不过在此之前,我们得构造WS服务的代理类,它是什么样子的呢?我们使用WCF框架,创建服务代理类也是十分简单的,常见的代理类结构如下:

    隐藏行号 复制代码 服务调用代理类
    1. [ServiceContract(Namespace="http://tempuri.org/")]
      
    2. public interface TestService
      
    3. {
      
    4.     [OperationContract(Action = "http://tempuri.org/HelloWorld", ReplyAction = "http://tempuri.org/HelloWorldResponse")]
      
    5.     string HelloWorld();
      
    6. }
      
    7. public class TestServiceClient : ClientBase<TestService>, TestService
      
    8. {
      
    9.     public TestServiceClient(Binding binding, EndpointAddress address) :
      
    10.         base(binding, address)
      
    11.     {
      
    12.     }
      
    13.     public string HelloWorld()
      
    14.     {
      
    15.         return base.Channel.HelloWorld();
      
    16.     }
      
    17. }
      
    18. 
      

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

    隐藏行号 复制代码 服务代理类构造参数
    1. public class WebServiceParamaters
      
    2. {
      
    3.     public string address;
      
    4.     public string Address
      
    5.     {
      
    6.         get { return address; }
      
    7.         set
      
    8.         {
      
    9.             address = value;
      
    10.         }
      
    11.     }
      
    12.     private string serviceNamespace;
      
    13.     public string ServiceNamespace
      
    14.     {
      
    15.         get { return serviceNamespace; }
      
    16.         set
      
    17.         {
      
    18.             serviceNamespace = value;
      
    19.         }
      
    20.     }
      
    21.     private string methodAction;
      
    22.     public string MethodAction
      
    23.     {
      
    24.         get { return methodAction; }
      
    25.         set
      
    26.         {
      
    27.             methodAction = value;
      
    28.         }
      
    29.     }
      
    30.     private string methodReplyAction;
      
    31.     public string MethodReplyAction
      
    32.     {
      
    33.         get { return methodReplyAction; }
      
    34.         set
      
    35.         {
      
    36.             methodReplyAction = value;
      
    37.         }
      
    38.     }
      
    39.     private string methodName;
      
    40.     public string MethodName
      
    41.     {
      
    42.         get { return methodName; }
      
    43.         set
      
    44.         {
      
    45.             methodName = value;
      
    46.         }
      
    47.     }
      
    48.     private string returnType;
      
    49.     public string ReturnType
      
    50.     {
      
    51.         get { return returnType; }
      
    52.         set
      
    53.         {
      
    54.             returnType = value;
      
    55.         }
      
    56.     }
      
    57. }
      
    58. 
      

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

    隐藏行号 复制代码 WebServiceProxyCreator
    1. public class WebServiceProxyCreator
      
    2. {
      
    3.     public Object WebServiceCaller(WebServiceParamaters parameters)
      
    4.     {
      
    5.         CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
      
    6.         CompilerParameters codeParameters = new CompilerParameters();
      
    7.         codeParameters.GenerateExecutable = false; 
      
    8.         codeParameters.GenerateInMemory = true; 
      
    9.         StringBuilder code = new StringBuilder();
      
    10.         CreateProxyCode(code, parameters);
      
    11. codeParameters.ReferencedAssemblies.Add("System.dll");
    12. codeParameters.ReferencedAssemblies.Add("System.ServiceModel.dll");
    13.         CompilerResults results = provider.CompileAssemblyFromSource(codeParameters, code.ToString());
      
    14.         Assembly assembly = null; 
      
    15.         if (!results.Errors.HasErrors)
      
    16.         {
      
    17.             assembly = results.CompiledAssembly;
      
    18.         }
      
    19.         Type clientType = assembly.GetType("RuntimeServiceClient");
      
    20.         ConstructorInfo ci = clientType.GetConstructor(new Type[] { typeof(Binding), typeof(EndpointAddress) });
      
    21.         BasicHttpBinding binding = new BasicHttpBinding(); //只演示传统的WebService调用
      
    22.         EndpointAddress address = new EndpointAddress(parameters.address);
      
    23.         Object client = ci.Invoke(new object[] { binding, address });
      
    24.         MethodInfo mi = clientType.GetMethod(parameters.MethodName);
      
    25.         Object result = mi.Invoke(client, null);
      
    26.         mi = clientType.GetMethod("Close"); //关闭代理
      
    27.         mi.Invoke(client, null);
      
    28.         return result;
      
    29.     }
      
    30.     public static void CreateProxyCode(StringBuilder code, WebServiceParamaters parameters)
      
    31.     {
      
    32.         code.AppendLine("using System;");
      
    33.         code.AppendLine("using System.ServiceModel;");
      
    34.         code.AppendLine("using System.ServiceModel.Channels;");
      
    35.         code.Append(@"[ServiceContract(");
      
    36.         if (!String.IsNullOrEmpty(parameters.ServiceNamespace))
      
    37.         {
      
    38.             code.Append("Namespace=\"").Append(parameters.ServiceNamespace).Append("\"");
      
    39.         }
      
    40.         code.AppendLine(")]");
      
    41.         code.AppendLine("public interface IRuntimeService");
      
    42.         code.AppendLine("{");
      
    43.         code.Append("[OperationContract(");
      
    44.         if (!String.IsNullOrEmpty(parameters.MethodAction))
      
    45.         {
      
    46.             code.Append("Action=\"").Append(parameters.MethodAction).Append("\"");
      
    47.             if (!String.IsNullOrEmpty(parameters.MethodReplyAction))
      
    48.             {
      
    49.                 code.Append(", ");
      
    50.             }
      
    51.         }
      
    52.         if (!String.IsNullOrEmpty(parameters.MethodReplyAction))
      
    53.         {
      
    54.             code.Append("ReplyAction=\"").Append(parameters.MethodReplyAction).Append("\"");
      
    55.         }
      
    56.         code.AppendLine(")]");
      
    57.         code.Append(parameters.ReturnType).Append(" ");
      
    58.         code.Append(parameters.MethodName).AppendLine("();");
      
    59.         code.AppendLine("}");
      
    60.         code.AppendLine();
      
    61.         code.AppendLine("public class RuntimeServiceClient : ClientBase<IRuntimeService>, IRuntimeService");
      
    62.         code.AppendLine("{");
      
    63.         code.AppendLine("public RuntimeServiceClient(Binding binding, EndpointAddress address) :base(binding, address)");
      
    64.         code.AppendLine("{");
      
    65.         code.AppendLine("}");
      
    66.         code.Append("public ").Append(parameters.ReturnType).Append(" ");
      
    67.         code.Append(parameters.MethodName).AppendLine("()");
      
    68.         code.AppendLine("{");
      
    69.         code.Append("return base.Channel.").Append(parameters.MethodName).AppendLine("();");
      
    70.         code.AppendLine("}");
      
    71.         code.AppendLine("}");
      
    72.     }
      
    73. }
      
    74. 
      

      注意,红色部分,由于代理类使用了WCF框架,所以编译时我们需要添加System.ServiceModel的引用,当然System.dll肯定是必须的,这里要注意,System.ServiceModel.dll应该保存到应用程序目录,否则动态编译时会引发异常,很简单,在工程引用中添加System.ServiceModel的引用,然后在属性中将拷贝到本地属性设置为true。

       到此,我们就可以直接通过传入的服务地址、服务方法名称以及相关的命名空间,即可调用服务(尽管我们只能调用无参服务,并且尽管我们也只能调用使用BasicHttpBinding绑定的服务,这些限制的原因是…我懒,好吧,相信只要经过一点改动即可去掉这些限制)。

       可惜,我们的程序还很傻:每次调用服务都需要去生成代码、编译、创建代理实例最后再调用,嗯…那就缓存吧:

       在WebServiceParameters类中重写GetHashCode方法:

    1:  public override int GetHashCode()
    2:  {
    3:      return String.Concat(serviceNamespace, methodAction, methodReplyAction, methodName, returnType).GetHashCode();
    4:  }
    5:   

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

     1:  public class WebServiceProxyCreator
     2:  {
     3:      private static Dictionary<int, Type> proxyTypeCatch = new Dictionary<int, Type>();
     4:   
     5:      public Object WebServiceCaller(WebServiceParamaters parameters)
     6:      {
     7:          int key = parameters.GetHashCode();
     8:          Type clientType = null;
     9:          if (proxyTypeCatch.ContainsKey(key))
    10:          {
    11:              clientType = proxyTypeCatch[key];
    12:              Debug.WriteLine("使用缓存");
    13:          }
    14:          else
    15:          {
    16:   
    17:              CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
    18:              CompilerParameters codeParameters = new CompilerParameters();
    19:              codeParameters.GenerateExecutable = false;
    20:              codeParameters.GenerateInMemory = true;
    21:   
    22:              StringBuilder code = new StringBuilder();
    23:              CreateProxyCode(code, parameters);
    24:   
    25:              codeParameters.ReferencedAssemblies.Add("System.dll");
    26:              codeParameters.ReferencedAssemblies.Add("System.ServiceModel.dll");
    27:   
    28:              CompilerResults results = provider.CompileAssemblyFromSource(codeParameters, code.ToString());
    29:              Assembly assembly = null;
    30:              if (!results.Errors.HasErrors)
    31:              {
    32:                  assembly = results.CompiledAssembly;
    33:              }
    34:   
    35:              clientType = assembly.GetType("RuntimeServiceClient");
    36:   
    37:              proxyTypeCatch.Add(key, clientType);
    38:          }
    39:          ConstructorInfo ci = clientType.GetConstructor(new Type[] { typeof(Binding), typeof(EndpointAddress) });
    40:          BasicHttpBinding binding = new BasicHttpBinding(); //只演示传统的WebService调用
    41:          EndpointAddress address = new EndpointAddress(parameters.address);
    42:          Object client = ci.Invoke(new object[] { binding, address });
    43:   
    44:          MethodInfo mi = clientType.GetMethod(parameters.MethodName);
    45:          Object result = mi.Invoke(client, null);
    46:          mi = clientType.GetMethod("Close"); //关闭代理
    47:          mi.Invoke(client, null);
    48:          return result;
    49:      }
    50:   
    51: }

    源码下载

  • 相关阅读:
    集群服务器登录退出出现问题
    TP框架中的Db::name 和 dB::table 以及 db('') 的区别
    TP5中orderRaw用法
    视差滚动 插件
    ThinkPad t480s 电源接口进水了
    file_put_contents failed to open stream: Permission denied in
    Mac 如何安装字体?
    ZipArchive::close(): Failure to create temporary file: Permission denied
    Wifi6 路由器推荐
    名词解释 | Enteric Nervous System | Enteric Neural Crest Cell | ENS | ENCC | 神经系统 | 神经嵴细胞
  • 原文地址:https://www.cnblogs.com/xfrog/p/2121963.html
Copyright © 2020-2023  润新知