实验讨论Atlas调用WebService时的复杂类型传递

近日拜读陈黎夫先生所著并发布在网上的《ASP.NET AJAX程序设计 第II卷:客户端Microsoft AJAX Library相关》的第三章《异步调用Web Service和页面中的类方法》(请参见陈先生博客http://www.cnblogs.com/dflying/archive/2007/06/12/780052.html),自己做了些实验以了解类型传递的原理,颇有些收获,现陈述如下,希望对ASP.NET AJAX的初学者有所帮助,并希望大家就其中不足多多给予指正。


实验1.服务端向客户端的传输

Step1.模仿陈先生在书中所举范例,可编写如下一个WebService类:

using  System;
using  System.Web;
using  System.Collections;
using  System.Web.Services;
using  System.Web.Services.Protocols;
using  System.Web.Script.Services;

public   class  Employee
{
    
private int m_id;
    
public int Id
    
{
        
get return m_id; }
        
set { m_id = value; }
    }


    
public string m_name;
    
public string Name
    
{
        
get return "My name is " + m_name + "!"; }
        
set { m_name = value; }
    }


    
private string m_email;
    
public string Email
    
{
        
get return m_email; }
        
set { m_email = value; }
    }


    
public int m_salary;
    [System.Web.Script.Serialization.ScriptIgnore]
    
public int Salary
    
{
        
get return m_salary; }
        
set { m_salary = value; }
    }


    
public Employee()
    
{
        ;
    }


    
public Employee(int id, string name, string email, int salary)
    
{
        m_id 
= id;
        m_name 
= name;
        m_email 
= email;
        m_salary 
= salary;
    }

}


[ScriptService]
[WebService(Namespace 
=   " Delete.Ra " )]
[WebServiceBinding(ConformsTo 
=  WsiProfiles.BasicProfile1_1)]
[GenerateScriptType(
typeof (Employee))]
public   class  WebService : System.Web.Services.WebService  {

    
public WebService () {

        
//如果使用设计的组件,请取消注释以下行 
        
//InitializeComponent(); 
    }


    [WebMethod]
    
public Employee CreateNewEmployee()
    
{
        
//返回一个已声明了GenerateScriptType的类
        return new Employee(0string.Empty, string.Empty, 0);
    }

    [WebMethod]
    
public bool SaveEmployee(Employee em)
    
{
        
// 保存到数据库中...
        return true;
    }

}

注意:其中与原文程序在访问权限上有微妙的不同。

Step2.在网页中调用CreateNewEmployee()方法

WebService.CreateNewEmployee(onSucceeded);

Step3.在程序中设置断点,并单步调试之。

稍加整理,可以得到如下的流程图(图中断点的符号表示被运行的语句或语句段):

SOAP中可以看出,
传给客户端的信息中只有
<名,值>的有序偶,而并没有将涉及对象内部的关联。
那么是不是在页面加载的一开始,类的内部关系就已经拷贝到客户端了呢?


实验2.类在客户端的样子

Step1.在客户端加入回调函数,获得新生成的类,并修改其值。

function  Button4_onclick()  {
    WebService.CreateNewEmployee(onSucceeded);
}

function  onSucceeded(result)  {
    
debugger;
    result.Name
="Delete";
    result.Email 
= "Delete.Ra@gmail.com";
    result.m_name
="Ra";
    
debugger;
}

注意:m_name应该是Name的内涵,在本程序中同时修改了这两个东西会发生什么事呢?加入断点进行调试。
属性被修改前:

属性被修改后:

可以看出,在客户端m_name和Name并无联系,Name属性对于客户端来说与m_name是对等的,都是简单的<名,值>关系。至于本程序中矛盾地对二者进行赋值到了服务端会怎么样请见实验3.2。

结论:
1.服务端的类在客户端并无内在联系。
2.能够表示成简单的<名,值>关系的数据才能被传递。

所以类的方法为什么不能传递就很好解释了。


实验3.1.从客户端向服务端传递(客户端篇)

Step1.在客户端加入代码:

function  pageLoad()  {
    
var e = new Employee();
    
debugger;
    e.Name 
= "Wo";
    e.Email
="Wp@abc.com";
    e.SpecialMember 
= 123;
    WebService.SaveEmployee(e);
}

值得注意的是,SpecialMember成员并没有在类的定义中出现,这样能行吗?运行一下试试。
运行到debugger语句:

(对象里什么都没有……)

赋值完毕:

(对象里面什么都有了,包括不该有的也有了……没办法,这是JavaScript的特性嘛~)

那么,这一数据能否顺利调用服务端的存储方法呢?测试一下webService的SaveEmployee方法就知道了:

POST /AJAXFuturesEnabledWebSite4/WebService.asmx HTTP/1.1
Host: localhost
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "Delete.Ra/SaveEmployee"

<? xml version="1.0" encoding="utf-8" ?>
< soap:Envelope  xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"  xmlns:xsd ="http://www.w3.org/2001/XMLSchema"  xmlns:soap ="http://schemas.xmlsoap.org/soap/envelope/" >
  
< soap:Body >
    
< SaveEmployee  xmlns ="Delete.Ra" >
      
< em >
        
< m_name > string </ m_name >
        
< m_salary > int </ m_salary >
        
< Id > int </ Id >
        
< Name > string </ Name >
        
< Email > string </ Email >
        
< Salary > int </ Salary >
      
</ em >
    
</ SaveEmployee >
  
</ soap:Body >
</ soap:Envelope >

上传的数据中压根就没有其它人什么事,所以不用担心SpecialMember这种异类会入侵到老家去。
但是万一要是客户端的程序把正确的属性名打错了一个字字母,那就废了,值又传不过去(SOAP所限),又不报错(Javascript所限),死活查不出问题。


实验3.2.从客户端向服务端传递(服务端篇)
继续跟踪服务端存储过程,可以得到如下流程图:

 

 还记得我们在实验2中做了一个奇怪的赋值吗?将作为内核的m_name先赋值,再给其外延Name属性赋不同的值,这样做在上传的SOAP中肯定没有问题,那里有它们两个的位置,但在服务端存储过程中它们这对水火不容的冤家会怎样呢?

刚刚调用完类的默认构造函数之后:

可以看到,在刚刚构建好一个空的对象之后,第一个属性的setter被调用前,m_name就已经有值了,该值是我们在客户端程序中直接给m_name赋的值。说明在这之间有一步(即图中步骤2),传递了同名成员变量的值。

调用完Name的setter之后:

属性值覆盖了成员变量的赋值。因此,我们知道在客户端的这种矛盾的赋值并不会引起什么大的异常,只是我们白赋了一个值,白传了一个参数,白白浪费了一点点带宽。

结论:如果WebService的类设计不当,让使用者既能操作成员变量又能操作包装该成员变量的属性,则很可能引起逻辑上的错误。


实验3.3.传递错误的对象

Step1.在服务端随便再建一个类
Step2.在客户端获取该服务器类,创建对象并进行赋值等操作
Step3.调用上述的SaveEmployee()方法,并以这个新建的“外星人”对象作为参数
Step4.跟踪运行过程


结果:程序运行到图中第5步,即调用WebService方法时,抛出类型异常,程序中止。
结论:白传了一组数据,服务端白跑了4步程序。


额外的结论:
客户端Javascript的松散,与服务端C#的严谨构成了一对矛盾,后台编写者必须为更加严谨地为前台提供松散的服务,否则客户端的胡乱使用会给网络和服务器造成不必要的负担。

一点问题:
不是说要传递复杂的类型就要加上[GenerateScriptType(typeof(Employee))]声明吗?为什么把该声明注释掉之后程序依然照常运行呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值