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

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

程序员文章站 2024-03-01 17:49:52
    动态编译与ws服务,有关系么?今天就乱弹一番,如何使用动态编译动态生成ws服务调用的代理类,然后通过这个代理类调用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="//www.jb51.net/")]
public interface testservice
{
    [operationcontract(action = "//www.jb51.net/helloworld", replyaction = "//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;
      }

 }