Reference from : http://blog.csdn.net/chuxiamuxiang/article/details/5731988
在C#程序中,若要调用WebService,一般是采用"添加Web引用"的方式来实现的。但如果此WebService的URL是在程序运行过程中才能获得的,那怎么办呢?那就必须是"动态"调用这个WebService了。
网上有不少关于这方面的例子,总结了一下,主要有两种:(1)每次都动态调用,(2)将WebService生成为一个本地的DLL,生成客户端的实例。
第一种方法:
我把它放到一个叫WebServiceHelper.cs的类里面了。
[c-sharp] view plaincopy
- /// <summary>
- /// 动态调用WebService
- /// </summary>
- /// <param name="url">WebService地址</param>
- /// <param name="classname">类名</param>
- /// <param name="methodname">方法名(模块名)</param>
- /// <param name="args">参数列表</param>
- /// <returns>object</returns>
- public static object InvokeWebService(string url, string classname, string methodname, object[] args)
- {
- string @namespace = "ServiceBase.WebService.DynamicWebLoad";
- if (classname == null || classname == "")
- {
- classname = WebServiceHelper.GetClassName(url);
- }
- //获取服务描述语言(WSDL)
- WebClient wc = new WebClient();
- Stream stream = wc.OpenRead(url + "?WSDL");
- ServiceDescription sd = ServiceDescription.Read(stream);
- ServiceDescriptionImporter sdi = new ServiceDescriptionImporter();
- sdi.AddServiceDescription(sd, "", "");
- CodeNamespace cn = new CodeNamespace(@namespace);
- //生成客户端代理类代码
- CodeCompileUnit ccu = new CodeCompileUnit();
- ccu.Namespaces.Add(cn);
- sdi.Import(cn, ccu);
- CSharpCodeProvider csc = new CSharpCodeProvider();
- ICodeCompiler icc = csc.CreateCompiler();
- //设定编译器的参数
- CompilerParameters cplist = new CompilerParameters();
- cplist.GenerateExecutable = false;
- cplist.GenerateInMemory = true;
- cplist.ReferencedAssemblies.Add("System.dll");
- cplist.ReferencedAssemblies.Add("System.XML.dll");
- cplist.ReferencedAssemblies.Add("System.Web.Services.dll");
- cplist.ReferencedAssemblies.Add("System.Data.dll");
- //编译代理类
- CompilerResults cr = icc.CompileAssemblyFromDom(cplist, ccu);
- if (true == cr.Errors.HasErrors)
- {
- System.Text.StringBuilder sb = new StringBuilder();
- foreach (CompilerError ce in cr.Errors)
- {
- sb.Append(ce.ToString());
- sb.Append(System.Environment.NewLine);
- }
- throw new Exception(sb.ToString());
- }
- //生成代理实例,并调用方法
- System.Reflection.Assembly assembly = cr.CompiledAssembly;
- Type t = assembly.GetType(@namespace + "." + classname, true, true);
- object obj = Activator.CreateInstance(t);
- System.Reflection.MethodInfo mi = t.GetMethod(methodname);
- return mi.Invoke(obj, args);
- }
- private static string GetClassName(string url)
- {
- string[] parts = url.Split(‘/‘);
- string[] pps = parts[parts.Length - 1].Split(‘.‘);
- return pps[0];
- }
举个使用它的例子:
[c-sharp] view plaincopy
- object[] args = new object[1];
- args.SetValue("cyy_JS", 0);
- DataTable dt = WebServiceHelper.InvokeWebService("http://192.168.0.10/DBMS_CYY/DBMS_Service.asmx", "GetUserTreeListData", args) as DataTable;
恩~有点麻烦,这意味着每次我都要把想调用的函数的参数组织成一个object[]才行,且每次调用InvokeWebService都是在内存中创建动态程序集,效率极低。则次种方法绝对没有直接用“实例名.方法名(参数列表)”来的舒服。
第二种方法:
为了提高效率,希望不要每次都创建这么一个动态程序集,那么就要把这个程序集保存在本地,使用的时候调用这个本地的DLL即可。
首先要调用:WebServiceHelper.CreateWebServiceDLL(); 用来在Debug目录下生成dll。这个函数的代码如下:
[c-sharp] view plaincopy
- public static void CreateWebServiceDLL()
- {
- // 1. 使用 WebClient 下载 WSDL 信息。
- WebClient web = new WebClient();
- Stream stream = web.OpenRead("http://192.168.0.10/DBMS_CYY/DBMS_Service.asmx?WSDL");
- // 2. 创建和格式化 WSDL 文档。
- ServiceDescription description = ServiceDescription.Read(stream);
- // 3. 创建客户端代理代理类。
- ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
- importer.ProtocolName = "Soap"; // 指定访问协议。
- importer.Style = ServiceDescriptionImportStyle.Client; // 生成客户端代理。
- importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync;
- importer.AddServiceDescription(description, null, null); // 添加 WSDL 文档。
- // 4. 使用 CodeDom 编译客户端代理类。
- CodeNamespace nmspace = new CodeNamespace(); // 为代理类添加命名空间,缺省为全局空间。
- CodeCompileUnit unit = new CodeCompileUnit();
- unit.Namespaces.Add(nmspace);
- ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit);
- CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
- CompilerParameters parameter = new CompilerParameters();
- parameter.GenerateExecutable = false;
- parameter.OutputAssembly = "DBMS_Service.dll"; // 可以指定你所需的任何文件名。
- parameter.ReferencedAssemblies.Add("System.dll");
- parameter.ReferencedAssemblies.Add("System.XML.dll");
- parameter.ReferencedAssemblies.Add("System.Web.Services.dll");
- parameter.ReferencedAssemblies.Add("System.Data.dll");
- CompilerResults result = provider.CompileAssemblyFromDom(parameter, unit);
- if (result.Errors.HasErrors)
- {
- // 显示编译错误信息
- System.Text.StringBuilder sb = new StringBuilder();
- foreach (CompilerError ce in result.Errors)
- {
- sb.Append(ce.ToString());
- sb.Append(System.Environment.NewLine);
- }
- throw new Exception(sb.ToString());
- }
- }
下面举个使用的例子:
[c-sharp] view plaincopy
- Assembly asm = Assembly.LoadFrom("DBMS_Service.dll");
- Type t = asm.GetType("DBMS_Service");
- object o = Activator.CreateInstance(t);
- MethodInfo method = t.GetMethod("GetUserTreeListData");
- object[] args = new object[1];
- args.SetValue("cyy_JS", 0);
- DataTable dt = method.Invoke(o, args) as DataTable;
这种方法的好处就是只用创建一次程序集,但缺点仍是调用函数的方式比较麻烦。
第二种方法升级版:
哈哈,这个是我自创的。目的就是为了解决:只创建一次DLL;以类似“实例名.方法名(参数列表)”的方式来调用函数;以这个DLL为模板,若WebService的内容有更新时,可同时更新这个本地的DLL。
代码如下:
[c-sharp] view plaincopy
- /// <summary>
- /// 根据WebService的URL,生成一个本地的dll,放在C盘下面,例如:C:|DBMS_WebService.dll
- /// 创建人:程媛媛 创建时间:2010-6-21
- /// </summary>
- /// <param name="url">WebService的UR</param>
- /// <returns></returns>
- public static void CreateWebServiceDLL(string url)
- {
- string @namespace = "ServiceBase.WebService.DynamicWebLoad";
- string classname = WebServiceHelper.GetClassName(url);
- // 1. 使用 WebClient 下载 WSDL 信息。
- WebClient web = new WebClient();
- Stream stream = web.OpenRead(url + "?WSDL");
- // 2. 创建和格式化 WSDL 文档。
- ServiceDescription description = ServiceDescription.Read(stream);
- // 3. 创建客户端代理代理类。
- ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
- importer.ProtocolName = "Soap"; // 指定访问协议。
- importer.Style = ServiceDescriptionImportStyle.Client; // 生成客户端代理。
- importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync;
- importer.AddServiceDescription(description, null, null); // 添加 WSDL 文档。
- // 4. 使用 CodeDom 编译客户端代理类。
- CodeNamespace nmspace = new CodeNamespace(@namespace); // 为代理类添加命名空间,缺省为全局空间。
- CodeCompileUnit unit = new CodeCompileUnit();
- unit.Namespaces.Add(nmspace);
- ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit);
- CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
- CompilerParameters parameter = new CompilerParameters();
- parameter.GenerateExecutable = false;
- parameter.OutputAssembly = "C://DBMS_Service.dll"; // 可以指定你所需的任何文件名。
- parameter.ReferencedAssemblies.Add("System.dll");
- parameter.ReferencedAssemblies.Add("System.XML.dll");
- parameter.ReferencedAssemblies.Add("System.Web.Services.dll");
- parameter.ReferencedAssemblies.Add("System.Data.dll");
- CompilerResults result = provider.CompileAssemblyFromDom(parameter, unit);
- if (result.Errors.HasErrors)
- {
- // 显示编译错误信息
- System.Text.StringBuilder sb = new StringBuilder();
- foreach (CompilerError ce in result.Errors)
- {
- sb.Append(ce.ToString());
- sb.Append(System.Environment.NewLine);
- }
- throw new Exception(sb.ToString());
- }
- }
首先调用CreateWebServiceDLL("http://192.168.0.10/DBMS_CYY/DBMS_Service.asmx")来生成“C:/DBMS_WebService.dll”;
接着,把这个DLL添加到工程的“引用”当中。
也许你会问,为什么不直接把DBMS_WebService.dll放在debug下?答案是,当把DBMS_WebService.dll添加为 引用时,若在程序运行过程中要更新(包括删了重新创建)它,则会报错。尝试了几次,被我发现,是不能放在Debug下的,其他的任何路径都可以,例如 bin目录下都可以,具体原因我不知道...
既然都把DBMS_WebService.dll添加为引用了,那么便可以先声明它了:
[c-sharp] view plaincopy
- public static ServiceBase.WebService.DynamicWebLoad.DBMS_Service pDBMS_Service;
注意:“public static ServiceBase.WebService.DynamicWebLoad.DBMS_Service”是我们调用 CreateWebServiceDLL("http://192.168.0.10/DBMS_CYY/DBMS_Service.asmx")的过程 中自定的表空间名。
接下来的事就是,需要一个函数,用来以此DBMS_WebService.dll为模板,用传入的URL来重新生成并覆盖这个DLL,并且,为了省 事,直接返回一个ServiceBase.WebService.DynamicWebLoad.DBMS_Service的实例。
代码如下:
[c-sharp] view plaincopy
- /// <summary>
- /// 根据WebService的URL,生成一个本地的dll,并返回WebService的实例
- /// 创建人:程媛媛 创建时间:2010-6-21
- /// </summary>
- /// <param name="url">WebService的UR</param>
- /// <returns></returns>
- public static object GetWebServiceInstance(string url)
- {
- string @namespace = "ServiceBase.WebService.DynamicWebLoad";
- string classname = WebServiceHelper.GetClassName(url);
- // 1. 使用 WebClient 下载 WSDL 信息。
- WebClient web = new WebClient();
- Stream stream = web.OpenRead(url + "?WSDL");
- // 2. 创建和格式化 WSDL 文档。
- ServiceDescription description = ServiceDescription.Read(stream);
- // 3. 创建客户端代理代理类。
- ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
- importer.ProtocolName = "Soap"; // 指定访问协议。
- importer.Style = ServiceDescriptionImportStyle.Client; // 生成客户端代理。
- importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync;
- importer.AddServiceDescription(description, null, null); // 添加 WSDL 文档。
- // 4. 使用 CodeDom 编译客户端代理类。
- CodeNamespace nmspace = new CodeNamespace(@namespace); // 为代理类添加命名空间,缺省为全局空间。
- CodeCompileUnit unit = new CodeCompileUnit();
- unit.Namespaces.Add(nmspace);
- ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit);
- CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
- CompilerParameters parameter = new CompilerParameters();
- parameter.GenerateExecutable = false;
- parameter.OutputAssembly = "C://DBMS_Service.dll"; // 可以指定你所需的任何文件名。
- parameter.ReferencedAssemblies.Add("System.dll");
- parameter.ReferencedAssemblies.Add("System.XML.dll");
- parameter.ReferencedAssemblies.Add("System.Web.Services.dll");
- parameter.ReferencedAssemblies.Add("System.Data.dll");
- CompilerResults result = provider.CompileAssemblyFromDom(parameter, unit);
- if (result.Errors.HasErrors)
- {
- // 显示编译错误信息
- System.Text.StringBuilder sb = new StringBuilder();
- foreach (CompilerError ce in result.Errors)
- {
- sb.Append(ce.ToString());
- sb.Append(System.Environment.NewLine);
- }
- throw new Exception(sb.ToString());
- }
- //生成代理实例
- System.Reflection.Assembly assembly = Assembly.Load("DBMS_Service");
- Type t = assembly.GetType(@namespace + "." + classname, true, true);
- object obj = Activator.CreateInstance(t);
- return obj;
- }
一切准备就绪,在主程序中这样使用它:
[c-sharp] view plaincopy
- object o = WebServiceHelper.GetWebServiceInstance(this.WebServiceURL);
- pDBMS_Service = o as ServiceBase.WebService.DynamicWebLoad.DBMS_Service;
- DataTable dt = pDBMS_Service.GetUserTreeListData(this.UserName);