华域联盟 .Net .NET的动态编译与WS服务调用详解

.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="https://www.cnhackhy.com/")]
public interface TestService

{

    [OperationContract(Action = "https://www.cnhackhy.com/HelloWorld", ReplyAction = "https://www.cnhackhy.com/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;

      }

 }

您可能感兴趣的文章:

  • 详细介绍.NET中的动态编译技术
  • 使用 C# 动态编译代码和执行的代码
  • C# 动态编译、动态执行、动态调试
  • .NET 动态编译
  • c#动态编译执行对象方法示例 运用映射机制创建对象

本文由 华域联盟 原创撰写:华域联盟 » .NET的动态编译与WS服务调用详解

转载请保留出处和原文链接:https://www.cnhackhy.com/51339.htm

本文来自网络,不代表华域联盟立场,转载请注明出处。

作者: sterben

发表回复

联系我们

联系我们

2551209778

在线咨询: QQ交谈

邮箱: [email protected]

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部