公众号推荐
微信公众号搜"智元新知" 关注 微信扫一扫可直接关注哦!
动态调用WebService(C#)
通常我们在程序中需要调用 WebService时,都是通过“添加 Web引用”,让VS.NET环境来为我们生成 服务代理,然后调用 对应的Web服务。这样是使工作简单了,但是却和提供Web服务的URL、方法 名、参数绑定在一起了,这是VS.NET自动 为我们生成 Web服务代理的限制。如果哪一天发布Web服务的URL改变了,则我们需要重新让VS.NET生成 代理,并重新编译。在某些情况下,这可能是不能忍受的,我们需要动态调用 WebService的能力。比如我们可以把Web服务的URL保存在配置文件 中,这样,当服务URL改变时,只需要修改 配置文件 就可以了。 说了这么多,实际上我们要实现这样的功能 :
public static object InvokeWebService( string url, string methodname, object [] args)
其中,url是Web服务的地址,methodname是要调用 服务方法 名,args是要调用 Web服务所需的参数,返回值就是web服务返回的结果了。
要实现这样的功能 ,你需要这几个方面的技能:反射、CodeDom、编程使用C#编译器、WebService。在了解这些知识后,就可以容易的实现web服务的动态调用 了:
#region InvokeWebService
object [] args)
{
return WebServiceHelper.InvokeWebService(url , null ,methodname ,args) ;
}
string classname,153); font-weight:bold; background-color:inherit">object [] args)
{
string @ namespace = "EnterpriseServerBase.WebService.DynamicWebCalling" ;
if((classname == null ) ||(classname == "" ))
{
classname = WebServiceHelper.GetWsClassName(url) ;
}
try
{
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();
CompilerPara meters cplist = new CompilerPara meters();
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 System .Text.StringBuilder();
foreach (System .CodeDom.Compiler.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,153); font-weight:bold; background-color:inherit">true ,153); font-weight:bold; background-color:inherit">true );
object obj = Activator.CreateInstance(t);
System .Reflection.MethodInfo mi = t.getmethod (methodname);
return mi.Invoke(obj,args);
catch(Exception ex)
new Exception(ex.InnerException.Message,153); font-weight:bold; background-color:inherit">new Exception(ex.InnerException.StackTrace));
}
private string GetWsClassName( string wsUrl)
string[] parts = wsUrl.Split( '/' ) ;
string[] pps = parts[parts.Length-1].Split( '.' ) ;
return pps[0] ;
#endregion
上面的注释已经很好的说明了各代码 段的功能 ,下面给个例子看看,这个例子是通过访问http://www.webservicex.net/globalweather.asmx 服务来获取 各大城市的天气状况。
string url = "http://www.webservicex.net/globalweather.asmx" ;
string [] args = new string [2] ;
args[0] = this .textBox _CityName.Text ;
args[1] = "China" ;
object result = WebServiceHelper.InvokeWebService(url , "GetWeather" ,153); font-weight:bold; background-color:inherit">this .label_Result.Text = result.ToString() ;
上述的例子中,调用 web服务使用了两个参数,第一个 是城市的名字,第二个是国家的名字,Web服务返回的是XML文档,可以从其中解析出温度、风力等天气情况。 最后说一下,C#虽然仍属于静态语言之列,但是其动态能力也是很强大的,不信,你可以看看Spring.net的AOP实现,这种“无侵入”的AOP实现比通常的.NET声明式AOP实现(一般是通过AOP Attribute)要漂亮的多。
using System ;
using System .Collections.Generic;
using System .Text;
using System .Xml;
using System .Net;
using System .Web.Services.Description;
using System .CodeDom;
using System .CodeDom.Compiler;
using System .Reflection;
namespace WindowsServiceWebDefaultHotCity
{
/// </summary<
class WebServiceAgent
{
object agent;
private Type agentType;
const string CODE_NAMESPACE = "Beyondbit.WebServiceAgent.Dynamic" ;
/// <para m name="url"<</para m<
public WebServiceAgent( string url)
{
XmlTextReader reader = new XmlTextReader(url + "?wsdl" );
ServiceDescription sd = ServiceDescription.Read(reader);
ServiceDescriptionImporter sdi = sdi.AddServiceDescription(sd, null ,153); font-weight:bold; background-color:inherit">null );
//使用 CodeDom 编译客户端代理类
CodeNamespace cn = new CodeNamespace(CODE_NAMESPACE);
CodeCompileUnit ccu = ccu.Namespaces.Add(cn);
sdi.Import(cn,108); list-style:decimal-leading-zero outside; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> Microsoft.CSharp.CSharpCodeProvider icc = new Microsoft.CSharp.CSharpCodeProvider();
CompilerPara meters cp = new CompilerPara meters();
CompilerResults cr = icc.CompileAssemblyFromDom(cp, ccu);
agentType = cr.CompiledAssembly.GetTypes()[0];
agent = Activator.CreateInstance(agentType);
}
///<summary<
///调用 指定的方法
///</summary<
///<para m name="methodName"<方法 名,大小写敏感</para m<
///<para m name="args"<参数,按照参数顺序赋值</para m<
///<returns<Web服务的返回值</returns<
object Invoke( string methodName,153); font-weight:bold; background-color:inherit">para ms {
MethodInfo mi = agentType.getmethod (methodName);
return this .Invoke(mi, args);
///<summary<
///调用 指定方法
///</summary<
///<para m name="method"<方法 信息</para m<
///<para m name="args"<参数,按照参数顺序赋值</para m<
///<returns<Web服务的返回值</returns<
object Invoke(MethodInfo method,153); font-weight:bold; background-color:inherit">return method.Invoke(agent,153); font-weight:bold; background-color:inherit">public MethodInfo[] Methods
get
{
return agentType.getmethod s();
}
}
}
}
using System .ComponentModel;
using System .Data;
using System .Drawing;
using System .Windows.Forms;
namespace WindowsApplication1
public partial class Form1 : Form
{
string _url = "http://www.baidu.com" ;
public Form1()
InitializeComponent();
init_Data();
void init_Data()
WindowsServiceWebDefaultHotCity.WebServiceAgent agent = new WindowsServiceWebDefaultHotCity.WebServiceAgent(_url);
object[] args = object [6];
args[0] = "PEK" ;
args[1] = "CAN" ;
args[2] = "" ;
args[3] = "2008-08-02" ;
args[4] = "00:00" ;
args[5] = "own_9588" ;
string text=agent.Invoke( "GetAllFlight" , args).ToString();
textBox 1.Text = text;
}
}
我们都知道,调用 WS可以在工程中添加 对WS的WEB引用。
但是,如果我们不想通过添加 引用的方式,而是在代码 中动态引用该怎么办呢?
首先,我们该想到WS的实现也是一个 类的形式。
其次,WS在传输过程中是通过WSDL来进行描述的(使用SOAP协议)。
因此,我们需要获取 WS的WSDL描述,并通过该描述来动态生成 程序集。
最后:通过反射来获取 新生成 的程序集,并调用 其方法 !
上述步骤需要引用如下四个名称 空间:
using System .Web.Services.Description;
using System .CodeDom;
using Microsoft.CSharp;
using System .CodeDom.Compiler;
上述几个名称 空间中包括 如下几个重要的类:
using System .Web.Services.Description下:
ServiceDescription
ServiceDescriptionImporter
以下是MSDN对其的描述:
XML Web services 的接口通常由 Web 服务描述语言 (WSDL) 文件 来说明。例如,若要获取 有关使用 http:
using System .CodeDom下:
Codedomunit
using System .CodeDom.Compiler下: CodedomProvider //用于创建和检索代码 生成 器和代码 编译器的实例,我们主要用到其实现子类CShareCodeProvider 可以直接用CShareCodeProvider provider=new CShareCodeProvider()来生成 ,或者用CodedomProvider.CreateProvider("CSharp")来生成 ICodeCompiler //用于编译基于 System .CodeDom 的源代码 表示形式。 它通过CodedomProvider的CreateCompiler()方法 来 CompilerResults //表示从编译器返回的编译结果。 它由ICodeCompiler根据指定的编译器设置从指定的 CodeCompileUnit 所包含的 System .CodeDom 树中编译程序集并返回。CompiledAssembly 属性 指示编译的程序集。
了解如上信息后,就可动态调用 WS了。 如下是摘自http://www.cnblogs.com/ruochen/archive/2007/12/11/990427.html 的代码 演示: Code
该方法 可以使程序不通过web引用的方式去调用 webservices方法 ,直接在代码 里调用 该方法 就能达到动态调用 webservices的目的。使用前先引用System .Web.Services动态链接 库,是.net自带 的dll。
方法 如下:
using System .Net;
using System .IO;
using Microsoft.CSharp;
using System .CodeDom.Compiler;
namespace TestSkin
{
class Webservices
/// <summary<
/// 实例化WebServices
/// </summary<
/// <para m name="url"<WebServices地址</para m<
/// <para m name="methodname"<调用 的方法 </para m<
/// <para m name="args"<把webservices里需要的参数按顺序放到这个object[]里</para m<
//这里的namespace是需引用的webservices的命名空间,在这里 是写死的,大家可以加一个 参数从外面传进来。
namespace = "client" ;
ServiceDescription sd = ServiceDescription.Read(stream);
string classname = sd.Services[0].Name;
ServiceDescriptionImporter sdi = new ServiceDescriptionImporter();
sdi.AddServiceDescription(sd, "" , "" );
CodeNamespace cn = namespace );
CodeCompileUnit ccu = new CodeCompileUnit();
ccu.Namespaces.Add(cn);
sdi.Import(cn,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px"> CSharpCodeProvider csc = new CSharpCodeProvider();
ICodeCompiler icc = csc.CreateCompiler();
//设定编译参数
CompilerPara meters cplist = 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,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px"> if ( true == cr.Errors.HasErrors)
{
System .Text.StringBuilder sb = new System .Text.StringBuilder();
foreach (System .CodeDom.Compiler.CompilerError ce in cr.Errors)
{
sb.Append(ce.ToString());
sb.Append(System .Environment.NewLine);
}
new Exception(sb.ToString());
}
//生成 代理实例,并调用 方法
System .Reflection.Assembly assembly = cr.CompiledAssembly;
Type t = assembly.GetType(@namespace + "." + classname,153); font-weight:bold; background-color:inherit">true );
object obj = Activator.CreateInstance(t);
System .Reflection.MethodInfo mi = t.getmethod (methodname);
catch
null;
}
===了解上述类和方法 后,基本就可以动态调用 WS了。 特别注意的是:动态编译后需要用到反射来读取并执行。因此需要您了解什么是反射及如何反射。
web service的动态调用 ,主要有三种方法 。 1、修改 config 文件 。只要你引用了web service,就会在config 文件 中出现asmx文件 的地址。只需要修改 该地址即可。 2、程序修改 url。web service是集成了System .Web.Service.WebService类的,而该类有一个 Url属性 。通过修改 该属性 可以达到与方法 一一样的效果 ,并且比它还要灵活。有的时候,我们需要提供一个 列表,有很多的服务器让用户 选择。程序根据用户 的选择连接到不同的服务器上调用 web service。这时,就可以用这个方法 了。 3、接口引用。有的时候,我们需要调用 不同服务器上的web service,但他们彼此又不一样,只是都实现了同一个 接口。这时候,就可以考虑下面的这个方法 。只要先引用所有的web service,然后用接口实例来保存创建出来的web service对象即可。 4、动态编译。这个应该算得上是真正意义上的动态了。有的时候,各个服务器上的web service更新比较快,我们不可能天天去更新代理类的,这个时候就可以用这个方法 了。 在该方法 中,有一点限制。就是各个服务器的web service,要么是都继承了同一个 接口,要么是都有一些同样签名的方法 ,而且service的类名最好是一样的。不过就算不符合这条件也没关系,后面我会在注释中注明的。看代码 :
using System ;
using System .CodeDom;
using System .CodeDom.Compiler;
using System .IO;
using System .Net;
using System .Reflection;
using System .Web.Services.Description;
using Microsoft.CSharp;
WebClient wc= new WebClient();
Stream stream = wc.OpenRead("http://localhost/TestService.asmx?WSDL" ); //这里指定你自己的web service url,一定要以?WSDL结尾
ServiceDescription sd = ServiceDescription.Read(stream);
ServiceDescriptionImporter sdi = new ServiceDescriptionImporter();
sdi.ProtocolName = "soap" ;
sdi.Style = ServiceDescriptionImportStyle.Client;
sdi.AddServiceDescription(sd,153); font-weight:bold; background-color:inherit">null );
//指定命名空间
CodeNamespace cn = new CodeNamespace( "Test" );
CodeCompileUnit ccu = new CodeCompileUnit();
ccu.Namespaces.Add(cn);
sdi.Import(cn, ccu);
建立C#编译器
CSharpCodeProvider csc = new CSharpCodeProvider();
ICodeCompiler icc = csc.CreateCompiler();
CompilerPara meters cp = new CompilerPara meters();
cp.GenerateExecutable = false ;
cp.GenerateInMemory = true ;
//添加 编译条件
cp.ReferencedAssemblies.Add("System .dll" );
cp.ReferencedAssemblies.Add("System .XML.dll" );
cp.ReferencedAssemblies.Add("System .Web.Services.dll" );
//编译程序集
CompilerResults cr = icc.CompileAssemblyFromDom(cp,0); background-color:inherit">//检查是否编译成功
if (!cr.Errors.HasErrors)
{
Assembly assembly = cr.CompiledAssembly;
//获取 程序集类型
//前面的Test就是命名空间,必须要与前面指定的一致
//后面的TestService就是service的类名
//如果所有的服务器都是一致的类名,这里就可以写死,否则要动态提供类名
Type type = assembly.GetType("Test.TestService" ,153); font-weight:bold; background-color:inherit">true );
object service = Activator.CreateInstance(type);
//获取 方法
//如果所有的服务器都是一致的方法 名,这里可以写死,否则就要动态提供方法 名
MethodInfo mi = type.getmethod ("HelloWorld" );
//调用 方法
//如果方法 没有参数,第二个参数可以传递null,否则就要传递object数组,数组元素的顺序要与参数的顺序一致
//如果所有服务器的方法 签名都是一致的,object数组的顺序就可以写死了,否则还要动态调整元素的数量 及顺序
mi.Invoke(service,0); background-color:inherit">//最后,返回的是object类型,根据方法 的签名,把返回值转换成不同的对象即可。
}
else
//这里自己处理编译错误
}
转载:http://blog.csdn.net/ysq5202121/article/details/6942813
方法 二:利用 wsdl.exe生成 webservice代理类:
根据提供的wsdl生成 webservice代理类,然后在代码 里引用这个类文件 。
步骤:1、在开始菜单 找到 Microsoft Visual Studio 2010 下面的Visual Studio Tools , 点击Visual Studio 命令提示 (2010),打开命令行。
2、 在命令行中输入: wsdl /language:c# /n:TestDemo /out:d:/Temp/TestService.cs http://jm1.services.gmcc.net/ad/Services/AD.asmx?wsdl
这句命令行的意思是:对最后面的服务地址进行编译,在D盘temp 目录下生成 testservice文件 。
3、 把上面命令编译后的cs文件 ,复制到我们项目中,在项目代码 中可以直接new 一个 出来后,可以进行调用 。
更为详细的可以参见:
http://www.voidcn.com/article/p-drlofvnv-dn.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。