设计模式学习——动态代理实现C#动态调用WebService(附源码)

对于这个问题,很很早以前就遇到了,当时并不理解。前段时间看了一下动态代理,对这个问题有了一些了解。

对于一般的webservice,可以通过添加web引用实现调用。但这样的缺点就是不够灵活,当webservice地址发生变化时需要重新添加引用,重新编译。这种缺点还稍微可以接受的。我遇到的应用场景是,程序运行之前无法知道webservice的地址,因为地址都存放于数据库中,使用时需要动态的调用。这样通过添加引用基本不可能实现,所以采用上述方法实现。

在采用这种方法时,首先遇到的问题就是:如何构造的代理类?

类似于javaJDK提供的有编译源文件的接口,.net也提供的有相关的类似的我们能够动态的生成源码并进行编译,从而动态的生成代理类。(当然不排除大牛通过解析语法规则直接生成二进制文件,而无需调用编译接口的。)

我们可以采用类似于下面的方法来动态的生成要编译的源码。

private CodeCompileUnit GetServiceCompileUnit(string webServiceUrl)

    {

        WebClient client = new WebClient();

        Stream stream = client.OpenRead(webServiceUrl);

//从这个url指向的的是一个xml文件,里面包含了该service的全部信息。

//进而通过解析xml文件从而可以生成要编译的源码。有兴趣的可以看一下xml的内容

ServiceDescription description = ServiceDescription.Read(stream);        ServiceDescriptionImporter importer = new ServiceDescriptionImporter();        importer.ProtocolName = "Soap";//使用的协议

        importer.Style = ServiceDescriptionImportStyle.Client;

importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync;

importer.AddServiceDescription(description, """");  

     CodeNamespace nmspace = new CodeNamespace();

        nmspace.Name = "WebService";//生成类的名空间,可以根据需求指定

        CodeCompileUnit unit = new CodeCompileUnit();

        unit.Namespaces.Add(nmspace);

        ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit);

        return unit;

    }

Unit返回的就是要进行编译的东西了。

 

下一步及时进行编译了。根据需求设置相关的编译参数后,就就可以进行编译了。就像下边这样。

 

 

 private CompilerResults Compile(CodeCompileUnit unit)

    {

        CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("CSharp");

        CompilerParameters compilerParameters = new CompilerParameters();

        compilerParameters.GenerateExecutable = false;

        compilerParameters.GenerateInMemory = true;

   //  cp.OutputAssembly = "D:\\Test.dll";这里也可以将变异的结果输出到dll文件中,从而可以查看编译的的结果。有兴趣的自己看一下。

     compilerParameters.ReferencedAssemblies.Add("System.dll");

        compilerParameters.ReferencedAssemblies.Add("System.XML.dll");

        compilerParameters.ReferencedAssemblies.Add("System.Web.Services.dll");

        compilerParameters.ReferencedAssemblies.Add("System.Data.dll");

  CompilerResults compilerResults = codeDomProvider.CompileAssemblyFromDom(compilerParameters, unit);

        if (compilerResults.Errors.HasErrors)

        {

            string errors = "";

            foreach (var item in compilerResults.Errors)

            {

                errors += item.ToString() + Environment.NewLine;

            }

            throw new Exception("Compile error:" + errors);

        }

        return compilerResults;

    }

 

有了编译的结果,已经生成了代理类,下一步要做的就是将这个代理类加载到内存当中。

   Assembly asm = result.CompiledAssembly;

        object obj = asm.CreateInstance("WebService." + proxy._className);

这样就将代理类加载到了内存当中,并且长生了一个实例,将这个实例返回就可以根据他来调用所需的方法了。当然调用方法时可能还是要用到反射,因为已知的可能仅仅只有方法名和它所需的参数。




本文转自HDDevTeam 51CTO博客,原文链接:http://blog.51cto.com/hddev/628288,如需转载请自行联系原作者

上一篇:Java动态代理学习2——静态代理和动态代理并对照spring的通知


下一篇:ATEC倒计时18天 | 也许,我们该重新认识蚂蚁金服(内赠门票)