【整理,内容来源于网络及本人实践】
前面整理了一篇关于VS2008实现简单的WebService的文章,但里面的调用是静态的,当服务地址更改后,需要重新编译程序,对于很多应用来说是不现实的。本文介绍一下C#动态调用WebService的方法。
1.实现
写个叫WebServiceHelper的类
namespace webhelp
{
using System;
using System.Net;
using System.IO;
using System.CodeDom;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Web.Services.Description;
using System.Web.Services.Protocols;
public class WebServiceHelper
{
#region InvokeWebService
public static object InvokeAndCallWebService(string url, string methodname, object[] args)
{
return WebServiceHelper.InvokeAndCallWebService(url, null, methodname, args);
}
public static object InvokeAndCallWebService(string url,/* string @namespace, */string classname, string methodname, object[] args)
{
string @namespace = "EnterpriseServerBase.WebService.DynamicWebCalling";
if ((classname == null) || (classname == ""))
{
classname = WebServiceHelper.GetWsClassName(url);
}
try
{
//获取WSDL
WebClient webClient = new WebClient();
Stream stream = webClient.OpenRead(url + "?WSDL");
ServiceDescription description = ServiceDescription.Read(stream);
ServiceDescriptionImporter descriptionImporter = new ServiceDescriptionImporter();
descriptionImporter.AddServiceDescription(description, "", "");
CodeNamespace codeNamespace = new CodeNamespace(@namespace);
//生成客户端代理类代码
CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
codeCompileUnit.Namespaces.Add(codeNamespace);
descriptionImporter.Import(codeNamespace, codeCompileUnit);
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
//设定编译参数
CompilerParameters compilerParameters = new CompilerParameters();
compilerParameters.GenerateExecutable = false;
compilerParameters.GenerateInMemory = true;
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 codeResult = codeProvider.CompileAssemblyFromDom(compilerParameters, codeCompileUnit);
if (true == codeResult.Errors.HasErrors)
{
System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
foreach (System.CodeDom.Compiler.CompilerError compileError in codeResult.Errors)
{
stringBuilder.Append(compileError.ToString());
stringBuilder.Append(System.Environment.NewLine);
}
throw new Exception(stringBuilder.ToString());
}
//生成代理实例,并调用方法
System.Reflection.Assembly assembly = codeResult.CompiledAssembly;
Type @class = assembly.GetType(@namespace + "." + classname, true, true);
object instance = Activator.CreateInstance(@class);
System.Reflection.MethodInfo methorInfo = @class.GetMethod(methodname);
return methorInfo.Invoke(instance, args);
}
catch (Exception ex)
{
throw new Exception(ex.InnerException.Message, new Exception(ex.InnerException.StackTrace));
}
}
private static string GetWsClassName(string wsUrl)
{
string[] parts = wsUrl.Split('/');
string[] pps = parts[parts.Length - 1].Split('.');
return pps[0];
}
#endregion
}
}
2.注意
1.)若编译提示“命名空间“System.Web”中不存在类型或命名空间名称“Services”(是缺少程序集引用吗?)”,只需在解决方案资源管理器中添加对System.Web.Services的程序集引用即可。
2.)不建议调用
public static object InvokeAndCallWebService(string url, string methodname, object[] args)
即不指定类名,让程序由URL中分析出类名,因为不是所有服务的类名都放在URL的最后一个‘/’和‘.’中间的。
3.)@namespace不一定要取"EnterpriseServerBase.WebService.DynamicWebCalling",从我的实验中,取任意@namespace,甚至@namespace是空串都能够正常运行。
4.)网上有人说用这种方法调用Java写的Webservice时有问题,说是异常提示报的是无法获得相应空间里的类,有人说跟Java不要命名空间有关。我特意请同学帮我做一个java 的webservice(因为我不会java),实验证明这种方法并没有什么问题,我猜测报异常可能是因为没有指定类名,而是代码根据url分析类名,或者有可能是服务返回类型是一些复杂类型的值,建议返回类型最好是string或int之类的。
5.)在这种方法中url、命名空间、类名、函数名都是字符串,可以根据需要进行赋值。
3.调用
注意到上面实现的InvokeAndCallWebService是个静态函数,所以想利用WebServiceHelper调用Webservice方法如下。
string url = "http://192.168.0.113/NM2SMARTB/INM2SmartSoap11Binding.asmx";
string[] args = new string[1];
args[0] = "hello";
object result = webhelp.WebServiceHelper.InvokeAndCallWebService(url, "Nm2Smart", "getDevices", args);
4.变化
namespace webhelp
{
using System;
using System.Net;
using System.IO;
using System.CodeDom;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Web.Services.Description;
using System.Web.Services.Protocols;
public class WebServiceHelper
{
#region InvokeWebService
object instance;//将原来函数内的局部变量变为类成员
Type @class;//将原来函数内的局部变量变为类成员
public void InvokeWebService(string url,/* string @namespace,*/ string classname)
{
string @namespace = "EnterpriseServerBase.WebService.DynamicWebCalling";
if ((classname == null) || (classname == ""))
{
classname = WebServiceHelper.GetWsClassName(url);
}
try
{
//获取WSDL
WebClient webClient = new WebClient();
Stream stream = webClient.OpenRead(url + "?WSDL");
ServiceDescription description = ServiceDescription.Read(stream);
ServiceDescriptionImporter descriptionImporter = new ServiceDescriptionImporter();
descriptionImporter.AddServiceDescription(description, "", "");
CodeNamespace codeNamespace = new CodeNamespace(@namespace);
//生成客户端代理类代码
CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
codeCompileUnit.Namespaces.Add(codeNamespace);
descriptionImporter.Import(codeNamespace, codeCompileUnit);
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
//设定编译参数
CompilerParameters compilerParameters = new CompilerParameters();
compilerParameters.GenerateExecutable = false;
compilerParameters.GenerateInMemory = true;
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 codeResult = codeProvider.CompileAssemblyFromDom(compilerParameters, codeCompileUnit);
if (true == codeResult.Errors.HasErrors)
{
System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
foreach (System.CodeDom.Compiler.CompilerError compileError in codeResult.Errors)
{
stringBuilder.Append(compileError.ToString());
stringBuilder.Append(System.Environment.NewLine);
}
throw new Exception(stringBuilder.ToString());
}
//生成代理实例
System.Reflection.Assembly assembly = codeResult.CompiledAssembly;
@class = assembly.GetType(@namespace + "." + classname, true, true);
instance = Activator.CreateInstance(@class);
// return instance;
}
catch (Exception ex)
{
throw new Exception(ex.InnerException.Message, new Exception(ex.InnerException.StackTrace));
}
}
public object CallService(string methodname, object[] args)//调用方法与生成代理实例分开
{
try
{
System.Reflection.MethodInfo methorInfo = @class.GetMethod(methodname);
return methorInfo.Invoke(instance, args);
}
catch (System.Exception ex)
{
throw ex;
}
}
#endregion
}
}