设计模式学习——动态代理实现C#动态调用WebService(附源码)
对于这个问题,很很早以前就遇到了,当时并不理解。前段时间看了一下动态代理,对这个问题有了一些了解。
对于一般的webservice,可以通过添加web引用实现调用。但这样的缺点就是不够灵活,当webservice地址发生变化时需要重新添加引用,重新编译。这种缺点还稍微可以接受的。我遇到的应用场景是,程序运行之前无法知道webservice的地址,因为地址都存放于数据库中,使用时需要动态的调用。这样通过添加引用基本不可能实现,所以采用上述方法实现。
在采用这种方法时,首先遇到的问题就是:如何构造的代理类?
类似于java中JDK提供的有编译源文件的接口,.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);
这样就将代理类加载到了内存当中,并且长生了一个实例,将这个实例返回就可以根据他来调用所需的方法了。当然调用方法时可能还是要用到反射,因为已知的可能仅仅只有方法名和它所需的参数。
更多详细内容请查看本文源码。
附件:http://down.51cto.com/data/2358642