http://www.dotblogs.com.tw/jimmyyu/archive/2009/04/22/8139.aspx

摘要

Web Service對大家來說想必都不陌生,也大都了解Web Service可以應用在哪些範圍,跨平台系統整合、跨語言整合、跨網域資料處理等繁雜的問題,在沒有Web Service前我們需要進行較繁雜的處理程序才能完成,但有了Web Service後,這樣的問題似乎很輕易的被解決了。

Web Service很方便,在.net領域,只要會寫程式的人大多可以透過VS輕易的完成一個Web Service,而呼叫端只要將這個Web Service加入WebReference後就可以使用這個Web Service中的WebMethod,在一般的應用下是這個樣子的,但在以下這個狀況,我們要使用Web Service會遭遇到一些這個問題:

※在擁有Web Service參考且發行過的站台中再次加入新的Web Service參考

而本文件的做法可以克服到以上問題。

一般Web Service的做法

建立Web Service

當我們建立一個Web Service時,我們會看到以下內容,VS會先為我們建立一個HelloWorld的WebMethod。

[WebService(Namespace = [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
...{
        ...{
                    }
    [WebMethod]
        ...{
            }
}

加入Web參考

當我們想要引用上述的WebMethod時,我們會採用『加入Web參考』的方式將Web Service參考進來:

接著將此Web參考命名為HelloService:

加入參考後,我們就可以在站台的App_WebReferences中看到這個Web Service:

使用Web Service

要使用剛剛建立的WebReference只要加入以下的程式碼就可以呼叫這個Web Service了:

HelloService.Service tService = string tMsg = tService.HelloWorld();


問題起源

發行網站

若這是一個要提供給客戶的無Source網站,我們通常會進行『發行』,發行後每個網頁的後端程式會被編譯成一個dll檔,而App_WebReferences目錄也會被編譯成一個App_WebReferences.dll,如下圖:

在已發行站台中加入新的Web參考

我們將發行過的網站開啟,我們可以看到這個站台中有一個PrecompiledApp.config的檔案,代表這個站台已先行編譯過了,如下圖:

接著我們執行先行編譯站台的Default.aspx,OK,開的起來,代表這個站台發行過程中沒有出現任何問題,接著我們為這個新站台加入另一個Web Service參考『localhost』,加入過程很順利,也沒有發生任何阻礙,

但當我再次嘗試開啟Default.aspx時會出現這個錯誤:

錯誤的訊息告知:不允許使用目錄’/TestInvokeWebservice/App_WebReferences/’因為已先行編譯應用程式,該站台我們在先前的步驟中已經發行過了,而發行過程我們也已經編譯過該網站。

問題發生的原因

ASP.NET 2.0在佈署上提供了兩種方式:

1. Source佈署:直接將Source放在站台上,透過動態編譯的方式compile程式

2. 無Source佈署:即先行編譯,會先將cs端的程式先compile成dll,避免程式外洩

現今架構下,產品多走2.,專案可能多走1.,在2的佈署架構下,我們會將我們的網站進行發行,發行後App_Code、App_WebReferences等ASP.NET目錄都會被各自compile成一個獨立的dll檔,並會直接放在發行後站台的bin目錄下,而dll的名稱是唯一的

當我們嘗試在一個已先行編譯過的網站中(若已包含App_Code.dll、App_WebReferences.dll)加入App_Code、App_WebReferences這兩個目錄,我們就會看到上頭的錯誤畫面。

所以我們遭遇到了一個問題,我們如何在先行編譯過的站台中加入新的Web Service參考?如果我今天接受到的就是別人已經發行過的網站,我如何去添加我想要的Web Service參考呢?

解決方案

針對此問題,我們的解決方案叫:動態叫用Web Service

您甚至不用將Web Service加入參考,你只要知道該Web Service的佈署路徑,要呼叫的function名稱等,這個作法就可以幫您呼叫到該Web Service,以下說明做法。

動態叫用function內容

在此架構下,我們寫了一個function做為動態叫用Web Service的服務,以下先說明此function的參數,本function有五個參數,內容分別如下,要特別說明的是pArgs這個參數,您需要將要呼叫的Web Service function所要的參數組成object[]傳進來:

/**/                                                                                                                                        WebClient tWebClient =                                 Stream tStream = tWebClient.OpenRead(pUrl +                 ServiceDescription tServiceDesp = ServiceDescription.Read(tStream);
                                ServiceDescriptionImporter tServiceDespImport =                 tServiceDespImport.AddServiceDescription(tServiceDesp,                 CodeNamespace tCodeNamespace =                                 CodeCompileUnit tCodeComUnit =                 tCodeComUnit.Namespaces.Add(tCodeNamespace);
                tServiceDespImport.Import(tCodeNamespace, tCodeComUnit);

                                CSharpCodeProvider tCSProvider =                 ICodeCompiler tCodeCom = tCSProvider.CreateCompiler();

                                System.CodeDom.Compiler.CompilerParameters tComPara = System.CodeDom.Compiler.CompilerParameters();
                tComPara.GenerateExecutable =                 tComPara.GenerateInMemory =
                                System.CodeDom.Compiler.CompilerResults tComResult =  
tCodeCom.CompileAssemblyFromDom(tComPara, tCodeComUnit);

                                                ...{
                    System.Text.StringBuilder tStr =                                         ...{
                        tStr.Append(tComError.ToString());
                        tStr.Append(System.Environment.NewLine);
                    }
                                    }

                                System.Reflection.Assembly tAssembly = tComResult.CompiledAssembly;
                Type tType = tAssembly.GetType(@pNamespace +                                                 Type[] tArgsType =                                 ...{
                    tArgsType =                 }
                                ...{
                                        tArgsType =                                         ...{
                        tArgsType[i] = pArgs[i].GetType();
                    }
                }

                System.Reflection.MethodInfo tInvokeMethod = tType.GetMethod(pMethodname, tArgsType);
                                }

動態叫用function的code sample

以上的source您可隨意利用,原則上這個寫法大致滿足多數應用,而以下我們再補充AP段的寫法,當我想要透過以上的function去幫我呼叫一個外部的Web service:

以下範例說明透過DynamicInvokeWebservice. InvokeWebservice去呼叫InvokeWS中的HelloWorld function,程式的寫法如下,傳入要呼叫的WS路徑、namespace、class name、function name、參數列表,然後呼叫InvokeWebservice就可以呼叫到遠端的Web service了。

                                                tArgs[0] =         tArgs[1] =
        DynamicInvokeWebservice tDynamicInvokeWebservice =         tDynamicInvokeWebservice.InvokeWebservice(tUrl, tNamespace, tClassname, tMethodname, tArgs);下面我們下了一個中斷點,經過測試後,我們確實可以呼叫到目標的Web service function,解決了我們前頭所遭遇到的問題:

後記

原則上這個作法應用的.net中的Reflection(反射)技術,若對這技術有興趣的人可以上MSDN參考。

版本修改:

如果要動態Invoke的Web Service是Windows整合驗證,那程式必須要做以下兩點修正,測試結果是沒問題的,如果Web Service有特別限定使用者的話(跨不同主機、跨Domain),請自行指定Credentials的帳號密碼:

1 WebClient tWebClient = new WebClient();
2 //要加這行:透過目前預設的使用者登入
3 tWebClient.Credentials = System.Net.CredentialCache.DefaultCredentials;
4 //讀取WSDL檔,確認Web Service描述內容
5 Stream tStream = tWebClient.OpenRead(pUrl + "?WSDL");
01 //若沒有overload的話,第二個參數便不需要,這邊要注意的是WsiProfiles.BasicProfile1_1本身不支援Web Service overload,因此需要改成不遵守WsiProfiles.BasicProfile1_1協議
02 System.Reflection.MethodInfo tInvokeMethod = tType.GetMethod(pMethodname, tArgsType);
03   
04 //要加這三行:如果是Windows整合驗證的話,透過SoapHttp來對要invoke的目標WS做驗證
05 SoapHttpClientProtocol webRequest = (SoapHttpClientProtocol)tTypeInstance;
06 webRequest.PreAuthenticate = true;
07 webRequest.Credentials = System.Net.CredentialCache.DefaultCredentials;
08   
09 //實際invoke該method
10 return tInvokeMethod.Invoke(tTypeInstance, pArgs);

參考資料:

HOW TO: 傳遞目前的憑證至 ASP.NET Web 服務

Dynamic Discovery and Invocation of Web services

baidu: 動態叫用Web Service site:dotblogs.com.tw

[C#]動態叫用Web Service的更多相关文章

  1. 篇章二:[AngularJS] 使用AngularAMD動態載入Service

    前言 「使用AngularAMD動態載入Controller」:這篇文章裡介紹如何使用AngularAMD來動態載入Controller.本篇文章以此為基礎,介紹如何使用AngularAMD來動態載入 ...

  2. JavaFX結合 JDBC, Servlet, Swing, Google Map及動態產生比例圖 (3):部署設定及應用 (转帖)

    說明:這一篇主要是說明如何將程式部署到Application Server,以及程式如何運作,產生的檔案置於何處,以及如何以瀏覽器呈現(Applet),或是當成桌面應用程式,或是 桌面Applet,這 ...

  3. JavaFX結合 JDBC, Servlet, Swing, Google Map及動態產生比例圖 (2):JavaFX建立及程式碼說明 (转帖)

    說明:就如同標題一樣,前端會用到JavaFX.Swing.Java Web Start.Google Map 的技術, 後端就是JDBC.Servlet的技術,以及我們會簽署認證jar檔案,這樣才可存 ...

  4. JavaFX結合 JDBC, Servlet, Swing, Google Map及動態產生比例圖 (1):NetBeans 寫 Servlet (转帖)

    JavaFX結合 JDBC, Servlet, Swing, Google Map及動態產生比例圖 (1):NetBeans 寫 Servlet 功能:這支程式的主要功能是將 javafx 與 swi ...

  5. 在Web Service中傳送Dictionary

    有個需求,想在Web Service中傳遞Dictionary<string, string>參數,例如: 排版顯示純文字 [WebMethod] public Dictionary< ...

  6. 问题:不支持Dictionary;结果:在Web Service中傳送Dictionary

    在Web Service中傳送Dictionary 有個需求,想在Web Service中傳遞Dictionary<string, string>參數,例如: 排版顯示純文字 [WebMe ...

  7. 篇章三:[AngularJS] 使用AngularCSS動態載入CSS

    前言 使用AngularAMD動態載入Controller 使用AngularAMD動態載入Service 上列兩篇文章裡,介紹了如何如何使用AngularAMD來動態載入Controller與Ser ...

  8. .NET基础拾遗(7)Web Service的开发与应用基础

    Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开发基 ...

  9. Web Service概念梳理

    计算机技术难理解的很多,Web Service 对我来说就是一个很难理解的概念:为了弄清它到底是什么,我花费了两周的时间,总算有了一些收获,参考了不少网上的资料,但有些概念说法不一.我以w3c和 一些 ...

随机推荐

  1. Android Service使用拾遗[阿里工程师分享]

    Service作为android的四大组件之一常用来帮助我们完成一些需要放在后台处理的任务,通过startService和bindService两种方式被调用.因为Service也是在主线程中运行的, ...

  2. CocoaPods:管理Objective-c 程序中各种第三方开源库关联

    在我们的iOS程序中,经常会用到多个第三方的开源库,通常做法是去下载最新版本的开源库,然后拖拽到工程中. 但是,第三方开源库的数量一旦比较多,版本的管理就非常的麻烦.有没有什么办法可以简化对第三方库的 ...

  3. Windows7下Blend for Visual Studio 2012使用问题

    目前开发的系统里很多控件样式和动画比较复杂,应该是之前同事用Blend做的,这种神器不用太浪费了,自己也准备试试. 系统环境Windows7+Visual Studio 2012 1.Windows7 ...

  4. iOS Xcode7上真机调试

    在Xcode7上进行真机调试,不需要证书,步骤如下: 1. 2. 3. 4. 5.添加Apple ID后会显示两个Free,表示可以免费真机调试iOS应用和Mac应用,选中高亮选项,点击view de ...

  5. QA:java.lang.RuntimeException:java.io.FileNotFoundException:Resource nexus-maven-repository-index.properties does not exist.

    QA:java.lang.RuntimeException:java.io.FileNotFoundException:Resource nexus-maven-repository-index.pr ...

  6. Entity Framework做IN查询

    开发中遇到的Too high level of nesting for select错误 项目使用了Entity Framework结合Mysql, 遇到了一个非常奇怪的性能问题,一个看起来非常简单的 ...

  7. Effective Java 16 Favor composition over inheritance

    Inheritance disadvantage Unlike method invocation, inheritance violates encapsulation. Since you don ...

  8. 从零开始学习Mysql的学习记录

    2015/06/18 16:23更新,由于QQ邮件的图片链接失效了,请在云笔记链接查看 http://note.youdao.com/share/?id=f0b2ed30a3fc8e57c381e3d ...

  9. [测试] 试用Hadoop 2.2中的HDFS NFS

    Hadoop 2.2中正式启用了hdfs nfs功能,使得hdfs的通用性迈进了一大步.在公司让小朋友搭建了一下,然后我自己进行了一点简单的试验,有一点收获,记录在此. 理论 使用hdfs nfs功能 ...

  10. Android ANR分析

    1.发生anr时手机会生产traces文件 拉取trace文件:adb pull data/anr/traces.txt ./mytraces.txt 保存路径 参考