转:[WebServices]介绍

1. 有关生存期的补充正常情况下,每次调用 WebMethod,服务器都会创建一个新的 WebService 对象,即便客户端使用同一个代理对象多次调用 WebMethod。而我们一旦调用了有缓存标记的 WebMethod,只要未超出缓存期,WebService 对象都不会被重新创建。在缓存期内调用没有缓存标记的 WebMethod,也会继续使用该 WebService 对象。有太多因素让这个缓存机制变得不那么可靠,因此我们不能奢望用缓存标记来维持特定的对象状态,况且缓存机制的设计初衷也只是为了快速输出那些比较稳定非常大的数据。基于多用户并发调用这个环境,WebService 本身最好设计成无状态对象,我们可以使用 Session 和 Application 来保持特定的状态信息。2. 异步调用网上很多人在写有关 .net 2.0 的文章时,都喜欢用“优雅”这个词。的确,在 2.0 中编译器和代码生成器为我们封装了很多罗嗦的东西,诸如匿名方法、委托推断等等,当然还有这 WebService 的异步调用。我们不用再写那些个 BeginXXX、EndXXX 了,基于事件驱动的异步机制会自动为每个 WebMethod 生成一个 XXXAsync 的异步方法和 XXXCompleted 事件,我们只需调用该方法,并处理该事件即可完成异步操作,当真是优雅了不少。不要小看 2.0 的这些封装,我们编写的代码越少意味着出错的几率越小。下面的示例中,我们使用了匿名方法来处理事件,看上去更简洁了些。WebServices.cs

[WebService(Namespace = "http://www.rainsts.net/",Description="我的Web服务")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]public class WebService : System.Web.Services.WebService {   [WebMethod]   public string HelloWorld()   {     return "Hello World!";   }}

Client.cs

WebService ws = new WebService();ws.HelloWorldCompleted += delegate(object sender,HelloWorldCompletedEventArgs e){   Console.WriteLine(e.Result);};ws.HelloWorldAsync("xxx");

3. 缓存WebMethodAttribute.CacheDuration 为 WebService 提供了缓存申明机制。通过添加该标记,我们可以缓存输出结果。不过缓存机制会影响 WebService 的生存期(见上)。WebServices.cs

[WebService(Namespace = "http://www.rainsts.net/",Description="我的Web服务")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]public class WebService : System.Web.Services.WebService {   [WebMethod(CacheDuration=10)]   public DateTime TestCache()   {     return DateTime.Now;   }}

Client.cs

WebService ws = new WebService();for (int i = 0; i < 20; i++){   Console.WriteLine("{0}:{1}",i + 1,ws.TestCache());   Thread.Sleep(1000);}

4. 保持状态.NET WebService 是建立在 ASP.NET 基础上,在 WebService 中我们同样可以访问 Session、User、Application 等上下文对象,不过在某些使用细节上可能有所不同。由于 WebService 客户端代理对象可能应用于 ConsoleApplication、WinForm 或 WebForm 等环境,而 Session 又必须通过 Cookie 来保存唯一的 SessionID,因此我们必须使用 CookieContainer 创建 Cookie 容器来保存 WebService 返回的 Session 信息,否则每次调用的 SessionID 都不同,自然无法使用 Session 来保存状态了。创建容器对象后,必须将其引用赋值给代理对象的 CookieContainer 属性。在第一次调用 SessionEnabled WebMethod 后,该容器将持有 Session Cookie 信息。如果需要在多个代理对象中调用 SessionEnabled WebMethod,那么它们必须持有同一个 Cookie 容器对象。WebServices.cs

[WebService(Namespace = "http://www.rainsts.net/",Description="我的Web服务")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]public class WebService : System.Web.Services.WebService {   [WebMethod(EnableSession = true)]   public string TestSession()   {     string s = "TestSession";     object o = Session[s];     int i = o != null ? (int)o : 0;     ++i;     Session[s] = i;     return Session.SessionID.ToString() + ":" + i;   }}

Client.cs

WebService ws = new WebService();// 创建Cookie容器,保持SessionID。否则每次调用的 SessionID 都不同。CookieContainer cookies = new CookieContainer();ws.CookieContainer = cookies;for (int i = 0; i < 10; i++){   Console.WriteLine("{0}:{1}",ws.TestSession());}

至于 Application 的使用和 WebForm 中基本没有什么区别。WebServices.cs

[WebService(Namespace = "http://www.rainsts.net/",Description="我的Web服务")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]public class WebService : System.Web.Services.WebService {   [WebMethod]   public DateTime TestApplicationState()   {     object o = Application["TestApplicationState"];     if (o == null)     {       o = DateTime.Now;       Application["TestApplicationState"] = o;     }     return (DateTime)o;   }}

Client.cs

for (int i = 0; i < 10; i++){   WebService ws = new WebService();   Console.WriteLine("{0}:{1}",ws.TestApplicationState());   Thread.Sleep(1000);}

5. SoapHeaderSoapHeader 多数情况下用来传递用户身份验证信息,当然它的作用远不止如此,有待于在实际应用中发掘。SoapHeader 缺省情况下由客户端代理对象发送给 WebService,当然我们可以通过 WebMethodAttribute.Direction 来改变传送方向。SoapHeader 使用步骤:(1) 创建继承自 System.Web.WebServices.SoapHeader 的自定义 SoapHeader 类型。(2) 在 WebService 中创建拥有 public 访问权限的自定义 SoapHeader 字段。(3) 在需要使用 SoapHeader 的 WebMethod 上添加 SoapHeaderAttribute 访问特性。SoapHeaderAttribute 构造必须指定 memberName 参数,就是我们在第二步中申明的字段名称。(4) 生成器会自动为客户端生成同名的自定义 SoapHeader 类型,只不过比起我们在 WebService 端创建的要复杂一些。同时还会为代理类型添加一个 soapheaderValue 属性。在下面的演示代码,客户端将传递一个自定义 MyHeader 到 WebService。请注意,我们尽管在 WebService 中申明了 MyHeader 字段,但并没有创建对象实例,这是因为客户端传递过来的 XML 中包含了 SoapHeader 信息,基础结构会自动解析并创建对象实例,然后赋值给 my 字段。至于客户端,自然需要创建一个 MyHeader 对象实例,并赋值给 WebService.MyHeaderValue 属性。SoapHeaderAttribute.Direction 缺省就是 In,下面例子中的 "Direction = SoapHeaderDirection.In" 可以省略。WebServices.cs

public class MyHeader : SoapHeader{   public string Username;   public string Password;}[WebService(Namespace = "http://www.rainsts.net/",Description="我的Web服务")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]public class WebService : System.Web.Services.WebService {   public MyHeader my;   [WebMethod]   [SoapHeader("my",Direction = SoapHeaderDirection.In)]   public void TestSoapHeadIn()   {     System.Diagnostics.Debug.Write(my.Username);     System.Diagnostics.Debug.Write(my.Password);   }}

Client.cs

WebService ws = new WebService();MyHeader head = new MyHeader();head.Username = "u2";head.Password = "p2";ws.MyHeadValue = head;ws.TestSoapHeadIn();

我们改写一下,将传递方向改为从 WebService 到客户端。自然我们需要调整 "Direction = SoapHeaderDirection.Out",在 WebMethod 中我们还必须创建 MyHeader 实例,因为这次我们不会接受到客户端传递的 SoapHeader 了。客户端代理对象调用 WebMethod 后就可以使用 MyHeaderValue 属性访问其内容了。WebServices.cs

public class MyHeader : SoapHeader{   public string Username;   public string Password;}[WebService(Namespace = "http://www.rainsts.net/",Direction = SoapHeaderDirection.Out)]   public void TestSoapHeadOut()   {     my = new MyHeader();     my.Username = "u1";     my.Password = "p1";   }}

Client.cs

WebService ws = new WebService();ws.TestSoapHeadOut();Console.WriteLine(ws.MyHeaderValue.Username);Console.WriteLine(ws.MyHeaderValue.Password);

6. 异常ASP.NET WebService 通过 Fault XML 元素来传递异常信息,客户端代理对象会生成一个 SoapException 的异常,并使用 Fault XML 信息填充其相关属性,诸如 Message 等。另外我们可以对 WebService 进行异常包装,除了传递 Exception Message 外,还可以传递一些错误状态代码,以便客户端用户做进一步处理。WebServices.cs

[WebService(Namespace = "http://www.rainsts.net/",Description="我的Web服务")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]public class WebService : System.Web.Services.WebService {   [WebMethod]   public void TestException()   {     try     {       throw new Exception("aaa...");     }     catch (Exception e)     {       throw new SoapException(e.Message,new System.Xml.XmlQualifiedName("ErrorCode01"),e);     }   }}

Client.cs

WebService ws = new WebService();try{   ws.TestException();}catch (System.Web.Services.Protocols.SoapException e){   Console.WriteLine(e.Message);   Console.WriteLine(e.Code.Name);}

ASP.NET WebService 支持绝大多数的基元类型及其数组,另外还支持自定义的结构(Struct)、类型(Class)、枚举(Enum)、DataSet、XmlElement、XmlNode、集合(IEnumerable/ICollection)等。ASP.NET WebService 使用 XmlSerializer 进行序列化操作,对于自定义类型要注意以下几点:1. 只能序列化可读写公共属性和字段。只读属性(get;)、只读字段(readonly)、常量(const)以及所有的非 public 数据成员都不会被序列化。2. 自定义类型必须具有不接受任何参数的默认构造函数。3. 不能序列化方法。客户端生成的代理对象不包含任何自定义类型方法(不是WebMethod)。基于以上几点,因此我们最好只定义纯粹用来传输复合数据的数据类型(Data Object)。以下是一些演示代码。基元类型

[WebService(Namespace = "http://www.rainsts.net/",Description="我的Web服务")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]public class WebService : System.Web.Services.WebService {public WebService () {}   [WebMethod]   public DateTime GetNowTime()   {     return DateTime.Now;   }   [WebMethod]   public string[] GetStringArray()   {     return new string[] { "a","b","c"};   }   [WebMethod]   public float[] GetFloatArray()   {      return new float[]{1F,2F,3F};   }   [WebMethod]   public byte[] GetBytes()   {     return System.IO.File.ReadAllBytes(@"c:\windows\notepad.exe");   }}

枚举 Enum

public enum Sex{   Female,   Male}[WebService(Namespace = "http://www.rainsts.net/",Description="我的Web服务")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]public class WebService : System.Web.Services.WebService {public WebService () {}   [WebMethod]   public Sex GetSex()   {     return Sex.Female;   }   [WebMethod]   public Sex[] GetAllSex()   {     return Enum.GetValues(typeof(Sex)) as Sex[];   }}

结构 Struct结构体默认就会创建无参数构造方法,且不允许自定义。

public struct MyStruct{   public int X;   public int Y;   public MyStruct(int x,int y)   {     this.X = x;     this.Y = y;   }}[WebService(Namespace = "http://www.rainsts.net/",Description="我的Web服务")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]public class WebService : System.Web.Services.WebService {public WebService () {}   [WebMethod]   public MyStruct GetMyStruct()   {     MyStruct st = new MyStruct(15,16);     return st;   }   [WebMethod]   public MyStruct[] GetMyStructs()   {     return new MyStruct[] { new MyStruct(1,2),new MyStruct(3,4) }; ;   }}

类型 Class

public struct MyStruct{   public int X;   public int Y;   public MyStruct(int x,int y)   {     this.X = x;     this.Y = y;   }}public class MyClass{   public MyClass()   {     myStruct = new MyStruct();   }   public MyClass(int x,int y,string name) : this()   {     myStruct.X = x;     myStruct.Y = y;     this.name = name;   }   private string name;   public string Name   {     get { return name; }     set { name = value; }   }   private MyStruct myStruct;   public MyStruct MyStruct   {     get { return myStruct; }     set { myStruct = value; }   }   public void Test() // 客户端代理不会生成该方法。   {     Console.WriteLine(name);   }}[WebService(Namespace = "http://www.rainsts.net/",Description="我的Web服务")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]public class WebService : System.Web.Services.WebService {public WebService () {}   [WebMethod]   public MyClass GetMyClass()   {     return new MyClass(1,2,"name1");   }   [WebMethod]   public MyClass[] GetMyClassArray()   {     return new MyClass[] { new MyClass(1,"name1"),new MyClass(2,3,"name2") }; ;   }}

有关数据类型的更详细信息,请查看 MSDN 文档。ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.VisualStudio.v80.chs/dv_fxwebservices/html/70567d9f-6e53-42a8-bbd5-aee42b25dd28.htm

多数时候我们通过 "添加 Web 引用..." 创建客户端代理类的方式调用WebService,但在某些情况下我们可能需要在程序运行期间动态调用一个未知的服务。在 .NET Framework 的 System.Web.Services.Description 命名空间中有我们需要的东西。具体步骤:1. 从目标 URL 下载 WSDL 数据。2. 使用 ServiceDescription 创建和格式化 WSDL 文档文件。3. 使用 ServiceDescriptionImporter 创建客户端代理类。4. 使用 CodeDom 动态创建客户端代理类程序集。5. 利用反射调用相关 WebService 方法。OK,看看具体的例子。我们要调用的目标 WebService,其 URL 是 HelloWorld.asmx

[WebService(Namespace = "http://www.rainsts.net/",Description="我的Web服务")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]public class WebService : System.Web.Services.WebService {public WebService () {}   [WebMethod]   public string HelloWorld()   {     return "Hello Wolrd!";   }}

1. 动态调用 WebService客户端动态调用代码

using System.IO;using System.Net;using System.Reflection;using System.CodeDom;using System.CodeDom.Compiler;using System.Web.Services;using System.Web.Services.Description;using System.Web.Services.Protocols;using System.Xml.Serialization;// 1. 使用 WebClient 下载 WSDL 信息。WebClient web = new WebClient();Stream stream = web.OpenRead("http://localhost:60436/Learn.WEB/WebService.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.GenerateInMemory = true;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);// 5. 使用 Reflection 调用 WebService。if (!result.Errors.HasErrors){   Assembly asm = result.CompiledAssembly;   Type t = asm.GetType("WebService"); // 如果在前面为代理类添加了命名空间,此处需要将命名空间添加到类型前面。   object o = Activator.CreateInstance(t);   MethodInfo method = t.GetMethod("HelloWorld");   Console.WriteLine(method.Invoke(o,null));}

2. 生成客户端代理程序集文件上面的代码通过在内存中创建动态程序集的方式完成了动态调用过程。如果我们希望将客户端代理类生成程序集文件保存到硬盘,则可以进行如下修改。生成程序集文件后,我们可以通过 Assembly.LoadFrom() 载入并进行反射调用。对于需要多次调用的系统,要比每次生成动态程序集效率高出很多。

using System.IO;using System.Net;using System.CodeDom;using System.CodeDom.Compiler;using System.Web.Services;using System.Web.Services.Description;using System.Web.Services.Protocols;using System.Xml.Serialization;// 1. 使用 WebClient 下载 WSDL 信息。WebClient web = new WebClient();Stream stream = web.OpenRead("http://localhost:60436/Learn.WEB/WebService.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,unit); CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");CompilerParameters parameter = new CompilerParameters();parameter.GenerateExecutable = false;parameter.OutputAssembly = "test.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){   // 显示编译错误信息}

调用程序集文件演示

Assembly asm = Assembly.LoadFrom("test.dll");Type t = asm.GetType("WebService"); object o = Activator.CreateInstance(t);MethodInfo method = t.GetMethod("HelloWorld");Console.WriteLine(method.Invoke(o,null));

3. 获取客户端代理类源代码还有一种情形,就是我们需要获得客户端代理类的 C# 源代码。

using System.IO;using System.Net;using System.CodeDom;using System.CodeDom.Compiler;using System.Web.Services;using System.Web.Services.Description;using System.Web.Services.Protocols;using System.Xml.Serialization;// 1. 使用 WebClient 下载 WSDL 信息。WebClient web = new WebClient();Stream stream = web.OpenRead("http://localhost:60436/Learn.WEB/WebService.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,unit); CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");// 5. 保存源代码到文件。当然,你也可以直接保存到内存字符串中。TextWriter writer = File.CreateText("test.cs"); // 指定你所需的源代码文件名。provider.GenerateCodeFromCompileUnit(unit,writer,null);writer.Flush();writer.Close();

如果你调用时触发 "WebException: 请求因 HTTP 状态 415 失败: Unsupported Media Type。" 这样的异常,那么恭喜你和我一样郁闷

[cry]

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


这篇文章主要讲解了“WPF如何实现带筛选功能的DataGrid”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“WPF...
本篇内容介绍了“基于WPF如何实现3D画廊动画效果”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这...
Some samples are below for ASP.Net web form controls:(from http://www.visualize.uk.com/resources/asp
问题描述: 对于未定义为 System.String 的列,唯一有效的值是(引发异常)。 For columns not defined as System.String, the only vali
最近用到了CalendarExtender,结果不知道为什么发生了错位,如图在Google和百度上找了很久,中文的文章里面似乎只提到了如何本地化(就是显示中文的月份)以及怎么解决被下拉框挡住的问题,谈
ASP.NET 2.0 page lifecyle ASP.NET 2.0 event sequence changed a lot since 1.1. Here is the order: App
静态声明: &#39; Style=&quot;position: relative&quot; AppendDataBoundItems=&quot;True&quot;&gt; (无 或 空 或
以下内容是从网络上搜集资料,然后整理而来的。不当之处,请不吝指教。(The following were from network, and edited by myself. Thanks in a
Imports System Imports System.Reflection Namespace DotNetNuke &#39;*********************************
Ok so you have all seen them: “8 million tools for web development”, “5 gagillion tools that if you
以下内容来源于: http://blog.csdn.net/cuike519/archive/2005/09/27/490316.aspx 问:为什么Session在有些机器上偶尔会丢失? 答:可能和
以下文章提到可以用“http://localhost/MyWebApp/WebAdmin.axd”管理站点: ---------------------------------------------
Visual Studio 2005 IDE相关的11个提高开发效率的技巧 英文原创来源于: http://www.chinhdo.com/chinh/blog/20070920/top-11-vis
C#日期格式化 from: http://51xingfu.blog.51cto.com/219185/46222 日期转化一 为了达到不同的显示效果有时,我们需要对时间进行转化,默认格式为:2007
from: http://www.nikhilk.net/UpdateControls.aspx Two controls that go along with the UpdatePanel and
Open the report in the Designer. In the ToolBox, select/expand the &quot;Report Items&quot; section.
from: http://drupal.org/node/75844 Do this: find which TinyMCE theme you are using. For the sake of
asp.net中给用户控件添加自定义事件 用户控件中定义好代理和事件: public delegate void ItemSavedDelegate(object sender, EventArgs
在Windows版本的Safari中浏览以下的页面。 http://www.asp.net/AJAX/Control-Toolkit/Live/Calendar/Calendar.aspx Calen
http://aspnet.4guysfromrolla.com/articles/021506-1.aspx By Scott Mitchell Introduction When creating