關於自定義實體類和Web服務之間的類型共享

由於個人習慣使用自定義實體類,所以在寫Web服務時常常返回的是實體類或是它的數組。剛開始還可以,但時間一長,老覺的默認的方法在共享類型上太麻煩。直到最近才發現只要小小的一點改變就可以解決問題。因爲自己已經走了很長時間的彎路,所以不希望大家也和小弟犯同樣的錯誤,就在此斗膽把自己的心得和一些想法寫出來。
 
舉例來說:
 有這樣的一個項目 RemoteGetObj
  裏面有3個子項目: 一個類庫 Obj_Lib;一個Web服務 RetObj_WebSer;一個WinForm ClientFrom
  其中 RetObj_WebSer ClientFrom 引用 Obj_Lib  ClientFrom又引用RetObj_WebSer Web服務
 
Obj_Lib 的數據定義如下:
<Serializable()> _
Public Structure BaseObj
    
Public sName As String
End Structure
 
Web 服務RetObj_WebSer 義如下
    <WebMethod()> _
    
Public 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() As BaseObj
            
Dim results() As Object = Me.Invoke("GetObjWebSer"New Object(-1) {})
            
Return CType(results(0),BaseObj)
        
End Function

    
<System.Xml.Serialization.XmlTypeAttribute([Namespace]:="http://tempuri.org/RemoteObject_WebSer/Service1")> _
    
Public Class BaseObj
        
        
'<remarks/>
        Public sName As String
End Class
 
 
在調用它的時候,GetObjWebSer 函數返回的是 [Web服務名].BaseObj,而不是它們共同引用的Obj_Lib.BaseObj。兩個類型雖然類型名稱一樣 並且 數據結構也完全一樣。但由於命名空間的不同,.Net會認爲他們完全不同,所以不能用等號直接拷貝。
以前一直不以爲這是個問題,無非再寫一次等量拷貝代碼,就是用一段機械性的代碼把一個類型複製到另一個代碼上。可後來發現隨着代碼量的上升和實體類數量的增加,這種笨方法無論在代碼的可讀性、維護性和結構上不行;更令人不能忍受的是在性能和內存使用上完全的失敗。遺憾的是,當發現問題時爲時已晚,大量的現有的重複代碼已經無法再修改了,所以當時用了個利用反射功能自動複製這些重複的類的方法 ,可無奈性能就此無法令人接受;最後在網上瘋狂學習,總算勉強搞了個用反射在運行時動態生成可執行代碼來完成複製類的功能,雖然穩定性不好,但畢竟混過了當時的任務。
在結束這次任務後,我馬上開始研究解決這個問題的辦法,最近總算有了些小想法,供大家參考.
1 、最簡單的方法: 修改代理類的源代碼。
   仔細看發現代理類函數不是直接生成類返回的
        Public Function GetObjWebSer() As BaseObj
            
Dim results() As Object = Me.Invoke("GetObjWebSer"New Object(-1) {})
            
Return CType(results(0),BaseObj)
        
End Function
   從上面可以看出幾個特點 Invoke 後的服務名必須和函數名一致,我曾嘗試修改函數名結果返回了個“服務名無效”的錯誤。
   第二行代碼 用Ctype來手動制定返回數據的類型。
   由此推測有如下可能:Invoke函數可能利用反射,通過第一個參數獲得當前函數的返回類型,再找到該類型的構造函數,創建一個該類型的實例。利用SOAP返回的XML,根據XML節點查找該類型是否有和SOAP裏的節點名稱一致的Field,有的話賦值,沒得話跳過。 那麼我們改動如下
        Public 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
 
結果,只要這麼小小的改動一下,Web服務的代理類就可以直接返回自定義的實體類。(當時發現時 狂吐血2小時,我的青春啊~~~白白浪費了)。後來再做了試驗,得到的結論是:不一定要原來的自定義實體類。任何類型,只要包含原來實體類裏有效數據相同的數據名稱就可了。Eg:
<Serializable()> _
Public Structure ClassA
    
Public sName As String
End Structure
 也可以
這種方法快速有效,但有個小問題。Web服務不是一次編寫就能好的,難不了要修改結構。
只要使用一次“更新Web引用”的命令,就又要修改一次原代碼
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章