您的位置:首页 > 其它

关于自定义实体类和Web服务之间的类型共享

2006-08-27 22:39 375 查看
由于个人习惯使用自定义实体类,所以在写Web服务时常常返回的是实体类或是它的数组。刚开始还可以,但时间一长,老觉的默认的方法在共享类型上太麻烦。直到最近才发现只要小小的一点改变就可以解决问题。因为自己已经走了很长时间的弯路,所以不希望大家也和小弟犯同样的错误,就在此斗胆把自己的心得和一些想法写出来。

举例来说:
有这样的一个项目 RemoteGetObj
[align=left] 里面有3个子项目: 一个类库 Obj_Lib;一个Web服务 RetObj_WebSer;一个WinForm ClientFrom[/align]
其中 RetObj_WebSer 和 ClientFrom 引用 Obj_Lib 而 ClientFrom又引用RetObj_WebSer Web服务

Obj_Lib 的数据定义如下:


<Serializable()> _




Public Structure BaseObjStructure BaseObj


Public sName As String


End Structure

Web 服务RetObj_WebSer 义如下


<WebMethod()> _




Public Function GetObjWebSer()Function GetObjWebSer() As Obj_Lib.BaseObj


Dim ret As Obj_Lib.BaseObj


ret.sName = "hello world"


Return ret


End Function

客户端 ClientFrom 如果按默认的Web服务代理生成的代码如下


'<remarks/> <System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/RemoteObject_WebSer/Service1/GetObjWebSer", RequestNamespace:="http://tempuri.org/RemoteObject_WebSer/Service1", ResponseNamespace:="http://tempuri.org/RemoteObject_WebSer/Service1", Use:=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle:=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)> _




Public Function GetObjWebSer()Function GetObjWebSer() As BaseObj


Dim results() As Object = Me.Invoke("GetObjWebSer", New Object(-1) {})


Return CType(results(0),BaseObj)


End Function




<System.Xml.Serialization.XmlTypeAttribute([Namespace ]Namespace]:="http://tempuri.org/RemoteObject_WebSer/Service1")> _




Public Class BaseObjClass BaseObj




'<remarks/>


Public sName As String


End Class

在调用它的时候,GetObjWebSer 函数返回的是 [Web服务名].BaseObj,而不是它们共同引用的Obj_Lib.BaseObj。两个类型虽然类型名称一样 并且 数据结构也完全一样。但由于命名空间的不同,.Net会认为他们完全不同,所以不能用等号直接拷贝。
以前一直不以为这是个问题,无非再写一次等量拷贝代码,就是用一段机械性的代码把一个类型复制到另一个代码上。可后来发现随着代码量的上升和实体类数量的增加,这种笨方法无论在代码的可读性、维护性和结构上不行;更令人不能忍受的是在性能和内存使用上完全的失败。遗憾的是,当发现问题时为时已晚,大量的现有的重复代码已经无法再修改了,所以当时用了个利用反射功能自动复制这些重复的类的方法 ,可无奈性能就此无法令人接受;最后在网上疯狂学习,总算勉强搞了个用反射在运行时动态生成可执行代码来完成复制类的功能,虽然稳定性不好,但毕竟混过了当时的任务。
在结束这次任务后,我马上开始研究解决这个问题的办法,最近总算有了些小想法,供大家参考.
1 、最简单的方法: 修改代理类的源代码。
仔细看发现代理类函数不是直接生成类返回的
[align=left][/align]




Public Function GetObjWebSer()Function GetObjWebSer() As BaseObj


Dim results() As Object = Me.Invoke("GetObjWebSer", New Object(-1) {})


Return CType(results(0),BaseObj)


End Function

[align=left] 从上面可以看出几个特点 Invoke 后的服务名必须和函数名一致,我曾尝试修改函数名结果返回了个“服务名无效”的错误。[/align]
第二行代码 用Ctype来手动制定返回数据的类型。
[align=left] 由此推测有如下可能:Invoke函数可能利用反射,通过第一个参数获得当前函数的返回类型,再找到该类型的构造函数,创建一个该类型的实例。利用SOAP返回的XML,根据XML节点查找该类型是否有和SOAP里的节点名称一致的Field,有的话赋值,没得话跳过。 那么我们改动如下[/align]

[align=left][/align]




Public Function GetObjWebSer()Function GetObjWebSer() As Obj_Lib.BaseObj


Dim results() As Object = Me.Invoke("GetObjWebSer", New Object(-1) {})


Return CType(results(0), Obj_Lib.BaseObj)


End Function

[align=left]结果,只要这么小小的改动一下,Web服务的代理类就可以直接返回自定义的实体类。(当时发现时 狂吐血2小时,我的青春啊~~~白白浪费了)。后来再做了试验,得到的结论是:不一定要原来的自定义实体类。任何类型,只要包含原来实体类里有效数据相同的数据名称就可了。Eg: [/align]


<Serializable()> _




Public Structure ClassAStructure ClassA


Public sName As String


End Structure

[align=left] 也可以[/align]
[align=left]这种方法快速有效,但有个小问题。Web服务不是一次编写就能好的,难不了要修改结构。[/align]
[align=left]只要使用一次“更新Web引用”的命令,就又要修改一次原代码。[/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: