.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: }

源码下载

你可能感兴趣的:(.net)