當(dāng)前位置:工程項目OA系統(tǒng) > 泛普各地 > 上海OA系統(tǒng) > 上海OA信息化
XML網(wǎng)絡(luò)服務(wù):使用SOAP和ASP.NET創(chuàng)建可復(fù)用網(wǎng)絡(luò)部件
XML網(wǎng)絡(luò)服務(wù):使用SOAP和ASP.NET創(chuàng)建可復(fù)用網(wǎng)絡(luò)部件
Andrew Clinick
Microsoft 公司
2001年9月10日
今年的早些時候,我介紹了于.NET 的Microsoft? Visual Studio? for Applications (VSA)
和腳本,并且介紹了如何使用這些技術(shù)來使你的.NET
應(yīng)用程序變得可以被自定義的。由于每個人對應(yīng)用程序應(yīng)該是什么樣子好像都有些微不同的想法,而不管它的.NET如何,因此,對一個.NET應(yīng)用程序進(jìn)行定義就成為一件多少有些不討好的事情。然而,通常一種技術(shù)包含在大多數(shù).NET應(yīng)用程序-網(wǎng)絡(luò)服務(wù)的定義中。因此,這個月我將介紹如何使用應(yīng)用于.NET
Framework的VSA和腳本
來創(chuàng)建可自定義的網(wǎng)絡(luò)服務(wù),而特別是如何能幫助創(chuàng)建一個下層結(jié)構(gòu),使你的用戶可以使用并配置可以對這個網(wǎng)絡(luò)服務(wù)進(jìn)行自定義的腳本代碼,并且從創(chuàng)建這個下層結(jié)構(gòu)獲利。
為什么要創(chuàng)建可自定義的應(yīng)用程序?
在介紹使用Visual Studio for Applications
來創(chuàng)建可自定義的應(yīng)用程序的技術(shù)細(xì)節(jié)前,我覺得我們最好回到前面并且看一看為什么在創(chuàng)建.NET
應(yīng)用程序時創(chuàng)建為可自定義的應(yīng)用程序是如此重要。如果你已經(jīng)對這個概念有所認(rèn)識,那么就簡單地跳過這節(jié)。
當(dāng)設(shè)計和編寫一個應(yīng)用程序時,你始終面對著你的用戶的要求,要為他們提供一個可以解決他們的問題并且通常使他們的生活更方便的程序。然而,不管你在創(chuàng)建這個程序的過程中付出多大努力去收集需求并和用戶交流,總是會有所完成的程序?qū)⒉荒芊纤杏脩舻男枨蟮慕Y(jié)論而放棄。需求不斷地變化是一貫如此的 (該死?。?-因為一旦用戶開始認(rèn)真地使用你的程序,他們就會不可避免地想到新的功能,這些功能是他們希望你的程序能實(shí)現(xiàn)來幫助他們工作。
如果你試圖去編寫將被不止一個公司使用的軟件,那么決定滿足哪個用戶的需求,解決哪個用戶的問題就變得十分困難。例如,如果你正在編寫一個訂單處理系統(tǒng),那么你能夠提供一個100%滿足你的客戶的恰當(dāng)?shù)纳虡I(yè)規(guī)則的機(jī)會是什么?對于一個沒有自定義的普通程序片斷不可能同時滿足所有用戶的需求。
你怎樣選擇來使你的程序可自定義的對你的用戶,對你的升級能力,同時對你的產(chǎn)品的銷售都會產(chǎn)生重大的影響。你有許多可選的選擇項目(重要性沒有特殊順序):
不提供自定義的能力
出于我已經(jīng)給出的所有原因,我將假定這不是一個很好的選擇。
為應(yīng)用程序提供源代碼,這樣你的客戶就可以對應(yīng)用程序進(jìn)行修改來滿足他們的需要
這提供了一些好處,其中從根本上是為你的客戶提供了完全的靈活性并且控制你的產(chǎn)品該如何執(zhí)行。這也許是一個可行的并且有吸引力的選項。然而,因此又冒出了許多問題,包括升級到新版本和保護(hù)你的知識產(chǎn)權(quán)的問題。我在這里不討論知識產(chǎn)權(quán)(沒有我的參與,在互聯(lián)網(wǎng)上也有足夠的人在討論這一點(diǎn)),但是升級問題對我們所有人都有影響。
例如,我們假設(shè)你提供了你的產(chǎn)品的1.0版的源代碼,而你的一些用戶更改了某些特殊部件的執(zhí)行。(我將用訂單處理系統(tǒng)的稅務(wù)計算模塊作為例子。)我們假定現(xiàn)在內(nèi)部的執(zhí)行事不同的,而這個模塊的外部界面被改為與客戶的內(nèi)部發(fā)票處理系統(tǒng)集成在一起。好的消息是,用戶現(xiàn)在已經(jīng)成功地實(shí)現(xiàn)把你的系統(tǒng)和他們的集成在一起,而且他們很高興。
在預(yù)計的時間內(nèi),你開始工作于你的產(chǎn)品的2.0版本,并且用戶也因為你添加的新功能而對購買新版本很有興趣。接下來你會去安裝產(chǎn)品的2.0版本,但是發(fā)現(xiàn)由于稅務(wù)計算模塊已經(jīng)被更改,除了試圖把你添加的更改和那些由用戶做的更改合并到一起外,沒有其他容易一些的方法來進(jìn)行升級。
從表面上看,這好像不是一個主要的問題,但是當(dāng)你增加系統(tǒng)各處的更改時,這就很快變成一個問題了。試想如果為了滿足客戶的需要對整個系統(tǒng)的各部分都進(jìn)行小更改。如果你試圖保留用戶已經(jīng)對系統(tǒng)所作的自定義的,那么提供一個從1.0版到2.0版的升級就變得令人吃驚的困難。實(shí)際上,這很快就會導(dǎo)致不能進(jìn)行升級,因此,無論用戶是否希望升級到最新版本,他們都被限制在你的軟件的老版本-而你也被困于試圖去對你的軟件的無數(shù)不同版本進(jìn)行支持。
為你的應(yīng)用程序提供腳本解決方案
把腳本創(chuàng)建到你的應(yīng)用程序中提供了一種機(jī)制,使你可以不用把你的應(yīng)用程序的源代碼公布給你的用戶就可以進(jìn)行自定義的。它也允許你在你的應(yīng)用程序中設(shè)計詳細(xì)的自定義的點(diǎn)。如果你要把在自定義的投資最大化,那么為你的應(yīng)用程序設(shè)計自定義的點(diǎn)大概是你的應(yīng)用程序設(shè)計中最重要的部分(并且有時要占用最多的時間)。一個好的自定義的設(shè)計過程將不僅僅在自定義的你的系統(tǒng)時節(jié)省時間,并且使你能夠?qū)δ切┮M(jìn)行自定義的和什么時候進(jìn)行自定義的進(jìn)行控制。
腳本方法優(yōu)于源代碼的最大好處就是自定義的可以在產(chǎn)品升級時得以保存。例如,改變稅務(wù)計算模塊執(zhí)行的腳本可以在所安裝的模塊的2.0版本中繼續(xù)使用(當(dāng)然,假設(shè)目標(biāo)模塊是向后兼容的),這是由于自定義的是腳本的一部分而不是這個模塊的完全實(shí)現(xiàn)。
Visual Studio for Applications提供了一種機(jī)制,它從把應(yīng)用于.NET Framework的腳本集成到你的應(yīng)用程序中獲利。也就是,它為你的客戶提供了一個全特性,集成的開發(fā)環(huán)境來編寫和調(diào)試腳本。
為客戶提供一種方法來把模塊聯(lián)接到你的應(yīng)用程序中
這是一種有趣的選擇,并且提供了許多腳本解決方案的好處,但是也給與你,應(yīng)用程序開發(fā)者和你的應(yīng)用程序的客戶很多挑戰(zhàn)。這項選擇有些類似于由Microsoft?
Office和Visual Studio
所提供的“添加”模塊。你的應(yīng)用程序提供的集成界面部件需要一種機(jī)制,就像在適當(dāng)?shù)臅r候把添加的部件加載到你的應(yīng)用程序中那樣。
如果承擔(dān)應(yīng)用程序自定義的任務(wù)的大多數(shù)人都是有經(jīng)驗的開發(fā)者,他們熟悉創(chuàng)建部件并且對接口有很好的理解,那么使用這種方法就是一種值得考慮的選擇。出于這個原因,我認(rèn)為這對腳本來說是一種免費(fèi)贈送的特性,并且不應(yīng)當(dāng)被看作是與把腳本添加到你的應(yīng)用程序中相抵觸。這就是說,當(dāng)開發(fā)一個應(yīng)用程序時就會涉及到許多挑戰(zhàn)-特別是許多執(zhí)行解決方案時涉及的工作。你需要開發(fā)一個集成接口,一種把模塊加載到你的應(yīng)用程序中的機(jī)制,并且提供這種集成機(jī)制的相關(guān)文檔。這并不需要很困難,但是如果你選擇腳本方法,它就是你不需要的東西。
開發(fā)可自定義的網(wǎng)絡(luò)服務(wù)
為了說明怎樣使用.NET Framework腳本和Visual Studio
for
Applications來創(chuàng)建一個可自定義的網(wǎng)絡(luò)服務(wù),我將建立一個用于scripthappens.com的簡單的計算網(wǎng)絡(luò)服務(wù)。這個網(wǎng)絡(luò)服務(wù)部件被故意做得很簡單,因此我就可以全神貫注于介紹如果集成腳本,而不是陷于處理稅務(wù)計算所需要的代碼中。這個網(wǎng)絡(luò)服務(wù)有兩個方法CalculateTax
和 CalculateDiscount, 并且將被scripthappens.com 電子商務(wù)網(wǎng)站的處理系統(tǒng)調(diào)用。服務(wù)自身完全在Microsoft?
Visual Basic? .NET 中執(zhí)行,但是用任何一種.NET語言都可以很好地開發(fā)。網(wǎng)絡(luò)服務(wù)提供了一個.NET
Framework腳本引擎運(yùn)行腳本來對系統(tǒng)運(yùn)行進(jìn)行自定義的,對腳本編寫者給予幫助,提供了一個代表了網(wǎng)絡(luò)服務(wù)接受到服務(wù)要求的狀態(tài)的對象模型。
網(wǎng)絡(luò)服務(wù)被設(shè)計成要從其他網(wǎng)絡(luò)應(yīng)用程序調(diào)用,包括所有在服務(wù)器和用戶機(jī)上的網(wǎng)絡(luò)應(yīng)用程序(Internet Explorer網(wǎng)絡(luò)服務(wù)行為)。由于使用完全是基于網(wǎng)絡(luò)設(shè)計的,所以我認(rèn)為所集成的Visual Studio for Applications 集成開發(fā)環(huán)境 (VSA IDE)可以通過網(wǎng)絡(luò)接口被調(diào)用也是很重要的。為了實(shí)現(xiàn)這一點(diǎn),我建立了一個簡單的系統(tǒng),它使用XML和MIME文件映射,可以通過在一個網(wǎng)絡(luò)瀏覽器上的網(wǎng)頁來啟動VSAIDE.后面更多地討論這個系統(tǒng)如何工作。
例子使用了Visual Studio for Applications 軟件開發(fā)工具包(VSASDK)來主機(jī).NET Framework引擎腳本和VSA IDE 。VSA SDK的關(guān)鍵設(shè)計點(diǎn)之一是允許應(yīng)用程序通過VSA來確定用戶編寫的腳本代碼的存儲格式和位置。為了實(shí)現(xiàn)這個目的,與SDK結(jié)合的主類向應(yīng)用程序開發(fā)者提供了一個機(jī)制,可以嵌入到開發(fā)者自己所堅持的機(jī)制中。這是通過編寫一個部件來實(shí)現(xiàn)的,這個部件是一個使用固定接口的代碼提供者,ICodeProvider ,在VSA SDK中定義。VSA SDK中的主類將把這個代碼管理器用于應(yīng)用程序中所有的固定和修補(bǔ)的腳本代碼中,而且都在設(shè)計時(從VSA IDE當(dāng)中)和運(yùn)行過程中腳本被加載時。
由于編寫VSA IDE中的腳本的人很可能與存儲和使用這個腳本代碼的人使用不同的機(jī)器,所以代碼管理器可以被遠(yuǎn)程調(diào)用就十分重要了。因此,VSA SDK被設(shè)計成遠(yuǎn)程使用代碼管理器,就像網(wǎng)絡(luò)服務(wù)通過HTTP協(xié)議使用SOAP。這個示例應(yīng)用程序使用一個代碼管理器作為VSA IDE的網(wǎng)絡(luò)服務(wù),而在運(yùn)行時要加載代碼來對網(wǎng)絡(luò)服務(wù)進(jìn)行自定義的時,在本地作為一個.NET部件。
圖 1.
Scripthappens.com 計算網(wǎng)絡(luò)服務(wù)的結(jié)構(gòu)
現(xiàn)在你該很希望對系統(tǒng)怎樣裝配到一起而成為一個整體有一個通常的理解,下面我就將進(jìn)入執(zhí)行的細(xì)節(jié)。
執(zhí)行網(wǎng)絡(luò)服務(wù)
計算網(wǎng)絡(luò)服務(wù)作為一個標(biāo)準(zhǔn).NET網(wǎng)絡(luò)服務(wù)被執(zhí)行,calculate.asmx
,和兩個方法在使用了WebMethod屬性的類中被定義為功能。WebMethod屬性告訴ASP.NET在運(yùn)行時放開這個方法,因此它可以被一個網(wǎng)絡(luò)服務(wù)調(diào)用-迄今為止沒有任何特例。
為了使網(wǎng)絡(luò)服務(wù)可以被自定義的,一個腳本引擎需要被加載來運(yùn)行任何用戶編寫的腳本代碼。在我最近的專欄中,我直接使用.NET Framework接口腳本,并且每次都調(diào)用源腳本代碼。這是很好的事情,由于它是一個客戶應(yīng)用程序,并且腳本代碼的性能并不是這個應(yīng)用程序主要考慮的問題。由于這是一個將用在一個服務(wù)器上的網(wǎng)絡(luò)服務(wù),無論如何,運(yùn)行時的性能都是關(guān)鍵。出于這個原因,我將使用.NET Framework引擎腳本的能力來加載預(yù)編譯代碼-特別是可以只調(diào)用予編譯代碼的輕-重量腳本引擎(light-weight script engine)(我在上個月當(dāng)作調(diào)用程序提到了這個引擎)。很幸運(yùn)的是VSA SDK提供了運(yùn)行時集成類,它使運(yùn)行引擎成為小菜一碟,因此我使用它把腳本代碼集成到網(wǎng)絡(luò)服務(wù)中。
VSA SDK運(yùn)行類被設(shè)計來提供一個IVsa接口的抽象,并且提供一個有效的途徑來把引擎集成到你的應(yīng)用程序中。由于你的應(yīng)用程序很可能在一大堆部件中集成這個腳本引擎,因此我們盡量保證代碼所需要使用的類的數(shù)量保持最少。我特別希望集成可以用三行代碼完成。(三行代碼對迷你代碼來說好像是很合適。雖然為什么三行代碼是重要數(shù)字已經(jīng)迷失在VSA設(shè)計過程的迷霧中。)
計算網(wǎng)絡(luò)服務(wù)的構(gòu)造函數(shù)創(chuàng)建了一個運(yùn)行庫和代碼管理器的實(shí)例,它將被用來給部件加載編譯過的腳本。我將在后面文章中介紹代碼管理器是怎樣執(zhí)行的,但是它是一個執(zhí)行ICodeProvider 的類,并且已經(jīng)被包括在項目中,所以就只是一個創(chuàng)建新實(shí)例的問題了。
運(yùn)行時類有許多重載構(gòu)造函數(shù)。既然這樣,我就使用了我認(rèn)為將是最常用的構(gòu)造函數(shù),它使用自定義的名字和將要用的名稱。名稱大概是VSA中最重要的概念之一,因為它對于.NET Framework腳本和VSA IDE來說都是至關(guān)重要的。實(shí)質(zhì)上,名稱是腳本代碼的唯一標(biāo)識,而你應(yīng)當(dāng)注意的是你如何去構(gòu)造它。名稱對于通過代碼管理器保存和重新找回任何代碼也是很重要的。一個代碼管理器實(shí)質(zhì)上是為你的應(yīng)用程序把一個名稱轉(zhuǎn)換為存儲機(jī)制。例如,這個例子中的代碼管理器把com.scripthappens://calculate 轉(zhuǎn)換為在當(dāng)前工作目錄下的一個層次文件夾vsa projectscalculate 。
一旦運(yùn)行時類的實(shí)例被創(chuàng)建,所有剩下的事情就是來提供一個代碼管理器的實(shí)例,類將使用這個實(shí)例來找回任意腳本代碼,告訴類去調(diào)用這段代碼,并且接下來運(yùn)行這段代碼-所有工作都在三行代碼中完成?。ê玫模虼宋也粫?chuàng)建的代碼管理器或類的實(shí)例進(jìn)行技術(shù),而他們不會記錄在我的代碼計數(shù)器的行數(shù)記錄當(dāng)中。)
Try
'
創(chuàng)建代碼管理器的新實(shí)例
myCodeProvider = New
DiskCodeProvider()
'
創(chuàng)建運(yùn)行時類的新實(shí)例
myRTClass = New Runtime("calculator",
"com.scripthappens://calculate")
'
把代碼管理器設(shè)置為磁盤代碼管理器的實(shí)例
myRTClass.SetCodeProviderLocal(myCodeProvider)
' 加載已編譯的代碼
myRTClass.Load(Nothing)
' 運(yùn)行代碼
myRTClass.Run()
Catch e As
Exception
'
拋出一個相當(dāng)沒用的意外來告訴用戶某些工作開始了
' 致命錯誤
Throw New Exception("Unable to load the
customization")
End Try
網(wǎng)絡(luò)服務(wù)現(xiàn)在已經(jīng)準(zhǔn)備好運(yùn)行任何用戶認(rèn)為合適的腳本了,但是迄今為止我們沒有為腳本提供任何對象模型來照著編寫。運(yùn)行時類提供了兩個方法來把對象添加到引擎中:AddObject 和AddEventSourceObject 。AddObject 把對象添加到引擎的全局范圍中,但是腳本代碼不能響應(yīng)任何由這些對象激發(fā)的事件。AddEventSourceObject 把對象添加到類或者模塊中,并且不使人驚奇的是,源于這個對象的事件可以被描述。
為了保持簡單化,計算網(wǎng)絡(luò)服務(wù)有一個激發(fā)事件的對象,稱為CalculateObjectModel (是不是很容易記住的名字?)。在這里我將與你一起在最前面,這里,在Beta 2版中有一個錯誤,為什么運(yùn)行時類要檢查是否你提供的對象實(shí)例非空。本應(yīng)當(dāng)引起注意在構(gòu)造函數(shù)中把eventsourceobject 添加到引擎中,但是我需要使用對象中的構(gòu)造函數(shù)來設(shè)置內(nèi)部只讀的屬性,基于把數(shù)據(jù)傳送到網(wǎng)絡(luò)的方法。由于那些數(shù)據(jù)在構(gòu)造時是不可得的,我必須把a(bǔ)ddeventsourceobject 調(diào)用添加到每個方法中。Visual Studio for Applications 最終版本沒有對實(shí)例的數(shù)據(jù)進(jìn)行檢查,這就允許我把一個空實(shí)例傳入并且在方法被調(diào)用時創(chuàng)建實(shí)例。
正確獲得對象模型
當(dāng)設(shè)計這個網(wǎng)絡(luò)服務(wù)時,我試著去想關(guān)于用戶怎樣才能進(jìn)行自定義的來符合他們的要求。同樣的,我花了許多時間來設(shè)計對象模型。這個我最終得到的模型是一個在稅務(wù)計算進(jìn)行前后都會激發(fā)事件的模型。我認(rèn)為這將給出更多的靈活性,由于它將運(yùn)行主要的事情-執(zhí)行你自己的稅務(wù)計算例程-并且也允許腳本作者做一些他們希望的后面的計算處理。
一旦我決定用這個前和后處理事件模型,我就開始考慮關(guān)于我如何裝配這些對象模型來使腳本編寫員的生活更輕松些-不影響到網(wǎng)絡(luò)服務(wù)的外部設(shè)計。一個重要的設(shè)計目標(biāo)是,在提供一個可以簡單地從腳本語言調(diào)用美國網(wǎng)絡(luò)方法來訪問內(nèi)部狀態(tài)的對象模型同時,確保網(wǎng)絡(luò)服務(wù)在實(shí)際執(zhí)行中保持本來的無國籍(stateless)。為了達(dá)到這個目的,我創(chuàng)建了一個新的類,它執(zhí)行腳本編寫者將會看到的對象模型,并且在美國網(wǎng)絡(luò)方法中使用它。
為了說明這個,我將解釋我怎樣在網(wǎng)絡(luò)服務(wù)中執(zhí)行CalculateTax 方法。這個方法相當(dāng)簡單。它得出一個總數(shù),和進(jìn)行稅務(wù)計算所得到的狀態(tài),作為證明。(我知道這是一個非常美國中心化的方法,但是它畢竟只是個演示。)為了確保網(wǎng)絡(luò)服務(wù)在執(zhí)行中保持本來的無國籍,總數(shù)和狀態(tài)信息不會送到方法的外部。無國籍編程對于可測量性來說有重要的好處,但是并不需要把變成變得復(fù)雜-特別是如果你不是一個有經(jīng)驗的程序員,這對于那些編寫腳本來自定義的你的應(yīng)用程序的人來說總是個問題。
為了幫助腳本編寫者,我創(chuàng)建了一個簡單的對象模型,它把當(dāng)前網(wǎng)絡(luò)方法的狀態(tài)作為只讀屬性表現(xiàn)出來(這些數(shù)值在類的構(gòu)造函數(shù)中設(shè)置)和有很多可以用來返回腳本自定義的結(jié)果的可讀/寫的屬性。除了屬性之外,這個對象也將給出腳本編寫者將用來對其變寫代碼的事件模型。我為這個前和后處理事件使用了一個簡單名字轉(zhuǎn)換BeforeEventName 和 AfterEventName 。
Public Class CalculateObjectModel
'
處理數(shù)據(jù)的成員變量
Private m_amount As
Decimal
Private m_state As String
Private m_taxAmount As Decimal
Private m_discount As
Decimal
Private m_custID As Integer
' 定義事件
Event BeforeCalculateTax()
Event AfterCalculateTax()
Event
BeforeCalculateDiscount()
Event
AfterCalculateDiscount()
'
用來設(shè)置總數(shù)和狀態(tài)的構(gòu)造函數(shù)
Public Sub New(ByVal amount As Decimal,
ByVal state As String)
m_amount =
amount
m_state =
state
m_custID =
Nothing
End Sub
' 用來設(shè)置總數(shù)和custID
的構(gòu)造函數(shù)
Public Sub New(ByVal amount As Decimal, ByVal custID
As Integer)
m_amount =
amount
m_custID =
custID
m_state =
Nothing
End Sub
'
為了讓編寫人員更簡單編寫代碼的一些屬性
Public Property Discount() As
Decimal
Get
Return
m_discount
End
Get
Set(ByVal Value As
Decimal)
m_discount = Value
End
Set
End Property
Public Property
Tax() As Decimal
Get
Return
m_taxAmount
End
Get
Set(ByVal Value As
Decimal)
m_taxAmount = Value
End
Set
End Property
Public ReadOnly
Property Amount() As Decimal
Get
Return
m_amount
End
Get
End Property
Public ReadOnly
Property state() As String
Get
Return
m_state
End
Get
End Property
Public ReadOnly
Property CustID() As Integer
Get
Return
m_custID
End
Get
End Property
End Class
為腳本提供對象模型
一個設(shè)計得很好的對象模型是偉大的,但是如果它不提供腳本引擎就是沒有用處的,而對象中的事件也不會按正確的順序被激發(fā)。網(wǎng)絡(luò)服務(wù)中的CalculateTax
方法在我的例子中不會真正地做很多事情,與創(chuàng)建對象模型的一個實(shí)例不同,在對象模型中激發(fā)事件BeforeCalculateTax
,從對象返回稅務(wù)總數(shù),并且激發(fā)AfterCalculateTax 事件。很明顯,一個真正的網(wǎng)絡(luò)服務(wù)將在激發(fā)事件間做更多的事情。
在方法的執(zhí)行過程中,我我遇到了一項有趣的挑戰(zhàn),而我認(rèn)為當(dāng)你開始編寫你的對象模型時也會遇到這個問題。也就是:我如何在一個類的新實(shí)例中產(chǎn)生事件?通常,你會使用RaiseEvent 方法來激發(fā)事件,但是不幸的是用來激發(fā)事件的代碼沒有運(yùn)行在對象模型的實(shí)例中,因此在對象模型和調(diào)用代碼間就需要一些階層的簡介聯(lián)系。
最初,我認(rèn)為這應(yīng)該是很容易解決的-只是在對象模型中執(zhí)行FireEvent 方法并且把你想激發(fā)的事件的名稱傳遞過去就行了,并且它就是那樣。像這樣做工作正常,但是有一個不幸的附加效果:方法FireEvent 暴露給腳本編寫者,這將導(dǎo)致一些相當(dāng)有趣的死鎖狀態(tài)。試想如果BeforeCalculateTax 的事件句柄與BeforeCalculateTax同時調(diào)用方法FireEvent -無限循環(huán)和咬牙切齒就跟著發(fā)生了。幸運(yùn)的是,這可以簡單地通過把方法FireEvent 設(shè)定為內(nèi)部的或Visual Basic用法中的“友”來避免,這樣做的意思是,方法會被運(yùn)行在同一集合中的代碼所看到而對于腳本代碼是不可見的。
Public Function CalculateTax(ByVal amount As Decimal,_
ByVal
state As String) As Double
'
為腳本創(chuàng)建對象模型的實(shí)例
'
并且在構(gòu)造函數(shù)中設(shè)置總數(shù)和狀態(tài)
CalcOM = New
CalculateObjectModel(amount, state)
Try
' 用VSA
Beta
2編程在引擎運(yùn)行前不能有EventSourceObject的空實(shí)例
myRTClass.AddEventSourceObject("CalculateCustomization",
"CalcOM",
CalcOM)
'
需要重置引擎來確保可以得到新eventsourceobject
myRTClass.Reset()
' 再次運(yùn)行代碼
myRTClass.Run()
Catch e As
Exception
' 拋出意外
Throw e
End Try
'
在對象模型中調(diào)用FireEvent方法來激活BeforeCalculateTax事件
CalcOM.FireEvent("BeforeCalculateTax")
'
當(dāng)然實(shí)際上你很可能在這里做一些通常的計算
'
把$1加到稅上來演示發(fā)生了一些事情
CalcOM.Tax +=
1
'
在對象模型中調(diào)用FireEvent方法來激發(fā)AfterCalculateTax事件
CalcOM.FireEvent("AfterCalculateTax")
'
從對象模型返回稅務(wù)計算的總數(shù)
Return
CalcOM.Tax
End Function
當(dāng)網(wǎng)絡(luò)服務(wù)被調(diào)用時,腳本代碼將真正的執(zhí)行稅務(wù)計算,并且服務(wù)將通過對象模型中的稅屬性從腳本返回結(jié)果。所有現(xiàn)在剩下的都是把腳本編寫者的能力添加到實(shí)際編寫腳本和把它存到服務(wù)器當(dāng)中。
為腳本提供開發(fā)環(huán)境
.NET Framework和VSA的腳本提供的超過Microsoft?
Windows?腳本的一個主要好處是有完全特性的VSA
IDE,它為腳本編寫者創(chuàng)建它們自己的自定義的腳本提供了第一流的編輯和調(diào)試環(huán)境。scripthappens.com 網(wǎng)絡(luò)服務(wù)無論在編輯和調(diào)試腳本代碼方面都從VSA
IDE獲得完全的好處。把VSA IDE集成到你的應(yīng)用程序中是通過為你要使用的腳本語言設(shè)計時引擎而完成的。(在Visual Studio for
Applications 1.0版中,我們只有時間去為Visual Basic .NET 設(shè)計事件引擎。)
設(shè)計時引擎的關(guān)鍵設(shè)計點(diǎn)是它應(yīng)該和用來控制像代碼項目和對象的腳本引擎(IVsa )有相同的接口(因此你就不必為了公共功能學(xué)習(xí)不同的主接口)。當(dāng)然它應(yīng)該也裝配一系列設(shè)計時接口(IVsaDT ),來提供對VSA IDE的訪問。VSA SDK提供了一個設(shè)計時集成類,它使得對VSA IDE集成變得容易,很大程度上與運(yùn)行時類使得主管運(yùn)行一個腳本引擎變得一樣的方法相同。
由于網(wǎng)絡(luò)服務(wù)沒有一個特定的Windows用戶,我設(shè)計了一個機(jī)制藉此VSA IDE可以從一個網(wǎng)絡(luò)瀏覽器來例示。由于VSA IDE可以被一系列接口所控制,在網(wǎng)頁中從腳本調(diào)用IDE就不是一個選擇了。Windows腳本只能通過IDispatch 調(diào)用對象,并且VSA IDE可以做一些潛在不安全的事情(像從磁盤上讀寫),因此這將不能與大多數(shù)網(wǎng)站的主要安全框架在一起工作。為了提供從一個瀏覽器對VSA IDC的訪問,我已經(jīng)從國際互聯(lián)網(wǎng)探索器的MIME處理特性得到好處來創(chuàng)建.NET Windows Forms應(yīng)用程序,解釋一個XML文件(由網(wǎng)絡(luò)服務(wù)器產(chǎn)生),并且使用IvsaDT接口來啟動VSA IDE。在我介紹程序怎樣做這些事情的細(xì)節(jié)之前,對Microsoft? Internet Explorer MIME句柄做些解釋在這種請況下就變得重要了。
Internet Explorer使用內(nèi)置的Windows能力來允許MIME所包含的類型與應(yīng)用程序聯(lián)合使用。MIME包括的類型是你在Windows中應(yīng)該很熟悉的相似文件擴(kuò)展名映射的超集。我創(chuàng)建一個MIME包含類型,VSA配置文件,并且用用擴(kuò)展名.vsa代表包含類型。 使用可以從文件夾選項控制面板小應(yīng)用程序(applet)得到的文件類型編輯器,我把MIME包含的類型與運(yùn)行VSA IDE的.NET應(yīng)用程序結(jié)合在一起。結(jié)果是,無論何時一個.vsa或VSA配置文件MIME包含類型被下載,VSA IDE主應(yīng)用程序都將被運(yùn)行,將解釋文件中的XML,并且使用包含在XML中的信息來加載腳本代碼并展示VSA IDE。這個方法當(dāng)然不是尖端科學(xué),并且在許多方面它的技術(shù)非常低。然而它為從一個網(wǎng)絡(luò)瀏覽器內(nèi)部運(yùn)行VSA IDE提供一個可擴(kuò)展的和一流的解決方案。
這種方法有一個很明顯的問題,然而:你怎樣把MIME映射和應(yīng)用程序添加到你的用戶機(jī)器上,如果他們在建立你的應(yīng)用程序之前就下載了.vsa文件呢?幸運(yùn)的是,Windows為這個提供了一個很好的解決方案,但是為了運(yùn)行,它要求你的用戶在有一個域服務(wù)器的內(nèi)部局域網(wǎng)上面工作。
如果你的機(jī)器聯(lián)接到一個域,這樣當(dāng)一個用戶下載一個已經(jīng)告知管理器的文件類型的文件時你就可以告知MIME和文件擴(kuò)展名管理器,并且他們不會安裝軟件,Windows會自動為他們安裝管理器。這意味著你可以確保你的用戶在下載一個.vsa類型文件時總是會另VSA IDE顯露出來。需要更多關(guān)于安裝這個解決方案的信息,可以查看Step-by-Step Guide to Software Installation and Maintenance 。
管理VSA IDE
現(xiàn)在我們已經(jīng)知道MIME管理器怎么能提供一種機(jī)制來幫助通過一個網(wǎng)絡(luò)瀏覽器管理VSA
IDE,那么讓我們看一看,.NET Windows Forms程序?qū)嶋H是怎樣使用VSA SDK 設(shè)計時類與VSA
IDE和代碼管理器進(jìn)行信息交換,來找到并存儲源腳本和已編譯的代碼。
這個例子中的主應(yīng)用程序的關(guān)鍵是在.vsa文件中包含的XML。我們所選擇用于例子的XML計劃提供了所有需要加載腳本代碼和腳本對象模型的信息。很明顯,由于網(wǎng)絡(luò)服務(wù)的對象模型是相當(dāng)知名的,因此我們本應(yīng)該很把它牢固地嵌入到應(yīng)用程序中。無論如何,我們認(rèn)為試圖開發(fā)一個你可以當(dāng)作你將來的Visual Studio for Applications項目的基礎(chǔ)的通用系統(tǒng)是重要的。例如,如果我們?yōu)閟cripthappens.com真正開發(fā)這個系統(tǒng),有一個我們可以在將來的項目中再次使用的解決方案將是件很不錯的事情。
XML模式例子
<application
name="calculator"
targeturl="http://localhost/ScriptHappens/default.aspx"
moniker="com.scripthappens://calculate"
language="Microsoft.VisualBasic.Vsa"
codeProviderURL="http://localhost/scripthappens/codeprovider.asmx"
>
<reference
name="ScriptHappens"
assembly="C:\Inetpub\wwwroot\scripthappens\bin\scripthappens.dll"
/>
<class name="Calculate"
>
<event name="calculateObjectModel"
type = "scripthappens.calculateObjectModel" />
</class>
</application>
這個程序的主要部分是應(yīng)用程序元素。里面包含所有程序相關(guān)的信息,包括名稱和自定義的名字,這應(yīng)該與運(yùn)行時集成相似。除了運(yùn)行時,targetURL和代碼管理器網(wǎng)絡(luò)服務(wù)外,設(shè)計時引入了許多概念。targetURL 用于用戶在他們的VSA IDE中運(yùn)行他們的代碼的時候。由于你的應(yīng)用程序?qū)嶋H上將控制用戶編寫的腳本代碼的運(yùn)行,而不是IDE,所以VSA IDE不能只是運(yùn)行代碼。
為了解決這個問題,IDE將回調(diào)主應(yīng)用程序來做加載腳本代碼所需要的事情。由于這個例子展示了如何在網(wǎng)絡(luò)服務(wù)中使用腳本,targetURL 就被添加到調(diào)用計算網(wǎng)絡(luò)服務(wù)的網(wǎng)頁中。這意味著在VSA IDE中運(yùn)行代碼將造成計算網(wǎng)絡(luò)服務(wù)被啟動,而任何用戶添加到他/她的腳本代碼中的斷點(diǎn)將造成IDE在網(wǎng)絡(luò)服務(wù)運(yùn)行時調(diào)試他們的腳本。
VSA Design-Time 類使用一個代碼管理器來處理腳本代碼的持久性,但是代替使用局部實(shí)例,它將把代碼管理器作為網(wǎng)絡(luò)服務(wù)來調(diào)用。對于VSA SDK來說這是關(guān)鍵的設(shè)計點(diǎn)。我們希望確保代碼的持久性已經(jīng)是可能的事情,并且特別是它要可以處理遠(yuǎn)程代碼存儲,而這些代碼也許是在防火墻后面,因為這對我們來說是關(guān)鍵的情節(jié)。為了通過一個網(wǎng)絡(luò)服務(wù)調(diào)用代碼管理器,所有你所需要的是使用方法SetCodeProviderRemote 并且給執(zhí)行IcodeProvider 的網(wǎng)絡(luò)服務(wù)提供URL。在例子中,我已經(jīng)讓代碼管理器在codeprovider.asmx 中執(zhí)行。這里我們感興趣的是,計算網(wǎng)絡(luò)服務(wù)在局部使用代碼管理器。那就是,它不會通過SOAP創(chuàng)建實(shí)例,但是使用與網(wǎng)絡(luò)服務(wù)相同的執(zhí)行。
設(shè)立設(shè)計時(Design Time) 引擎
從應(yīng)用程序部件獲得的信息會在創(chuàng)建設(shè)計時類時使用。設(shè)計時類與運(yùn)行時間類的設(shè)計多少有些不同。它需要可以允許主應(yīng)用程序管理任何引擎,因為VSA
IDE將被用來編輯系統(tǒng)中的代碼,并且這里將會有多于一個的引擎在系統(tǒng)中使用來提供自定義的。例如,如果在解決方案中有兩個網(wǎng)絡(luò)服務(wù),那么每個網(wǎng)絡(luò)服務(wù)中就會有一個腳本引擎。VSA
IDE將不得不對兩個腳本項目都進(jìn)行引導(dǎo)。結(jié)果是,設(shè)計時類提供了一個簡單的方法來創(chuàng)建多個設(shè)計時引擎。比通過引擎中的接口提供一個抽象要好,它只是傳回一個引擎的實(shí)例,你將依靠Ivsa和IvsaDT對其進(jìn)行編程。為了說明這一點(diǎn),我將不去注意代碼的要求來設(shè)立設(shè)計時引擎并運(yùn)行它。
XML程序?qū)⒃谥鲬?yīng)用程序的Windows Form的加載事件中進(jìn)行解析。為了對XML進(jìn)行解析,我使用了XML解析器,它作為.NET Framework的一部分使用,簡單地通過應(yīng)用程序部件的屬性重復(fù),并為后面使用設(shè)計時類存儲數(shù)據(jù)。為了提供一些什么在進(jìn)行的用戶反饋,程序使用Windows Form Progress Bar 控件并且把過程數(shù)值設(shè)置為只讀屬性。這相當(dāng)簡單,但是相當(dāng)有效。
'
打開在命令行輸入的XML文件
Dim xml As
XmlDocument
xml = New
XmlDocument()
xml.Load(args(1))
Dim node As
XmlNode
node = xml.SelectSingleNode("application")
Me.ProgressBar1.Value =
5
'
忽略whitespace
' myXML.WhitespaceHandling =
WhitespaceHandling.None
Me.ProgressBar1.Value = 10
'
得到名字
custName =
node.Attributes.ItemOf("name").Value
Me.ProgressBar1.Value = 15
'
得到targeturl
targetUrl =
node.Attributes.ItemOf("targeturl").Value
Me.ProgressBar1.Value =
20
' 得到名稱
moniker =
node.Attributes.ItemOf("moniker").Value
Me.ProgressBar1.Value = 25
'
得到引擎語言
language =
node.Attributes.ItemOf("language").Value
Me.ProgressBar1.Value = 30
'
得到代碼管理器URL
codeProviderURL =
node.Attributes.ItemOf
("codeProviderURL").Value
Me.ProgressBar1.Value = 35
一旦所有應(yīng)用程序部件的信息都被解析,程序就會有足夠的信息來開始使用設(shè)計時類。在scripthappens.com 類中,我創(chuàng)建了一個類,它擴(kuò)展了VSA 設(shè)計時類來使得集成更簡單。我當(dāng)然推薦你在使用設(shè)計時類的時候做相似的事情。這個類有一個把所有信息包含在應(yīng)用程序部件中的構(gòu)造函數(shù),因此它只關(guān)系到創(chuàng)建類的一個實(shí)例并把所有消息傳送到構(gòu)造函數(shù)中。
' 創(chuàng)建webhost類的實(shí)例
myhost = New dthost(Me, custName, moniker,
targetUrl, language,
codeProviderURL)
Me.ProgressBar1.Value = 60
類dthost
的構(gòu)造函數(shù)使用代碼管理器URL為設(shè)計時類設(shè)置代碼管理器來加載腳本代碼。作為防范,類要進(jìn)行檢查來看看URL是否為空和它是否使用局部代碼管理器。一旦代碼管理器被設(shè)立,設(shè)計時類就有了所有需要用來加載或創(chuàng)建一個VSA項目的信息,因此可以很安全地創(chuàng)建一個新的設(shè)計時引擎。設(shè)計時類被設(shè)計為可以很輕松地通過提供一個引擎的集合來處理多個引擎,VsaEngines
。這個集合有一個方法,create
,它將返回一個新引擎并且添加到這個集合中。在這個例子中,為了保持簡單,這里只使用了一個引擎,但是有一個集合將使你的生活稍微輕松一些。
'------------------------------------------------------------------
'
為這個引擎設(shè)置代碼管理器
'------------------------------------------------------------------
If "" = strCodeProviderURL
Then
SetCodeProviderLocal(New
DiskCodeProvider())
Else
SetCodeProviderRemote(strCodeProviderURL)
End If
一旦代碼管理器被設(shè)置完畢,主類就將查看這里是否已經(jīng)提供了這個名稱的存在地項目。這實(shí)現(xiàn)起來相當(dāng)簡單,只是調(diào)用基類VSA SDK DT中的方法 LoadEngineSource 并且抓住任何意外。如果項目已經(jīng)存在,那么我們就做得差不多了,因為項目中包含了對象模型和引用所需要的所有信息。然而如果項目不存在,那么我們就需要從集合中移走老的引擎,并且繼續(xù)白手起家創(chuàng)建新的引擎。新的引擎將從現(xiàn)在開始使用來添加代碼,對象模型和參考。在這階段,除了我們要對引擎進(jìn)行初始化就沒有別的什么事情了,因此代碼調(diào)用引擎中的方法InitNew 。這告訴引擎已經(jīng)進(jìn)行了初始化,而它可以繼續(xù)并且完成創(chuàng)建新引擎的過程。
Try
'------------------------------------------------------------------
'
試圖加載我們的引擎
'------------------------------------------------------------------
LoadEngineSource(strMoniker, Nothing)
'------------------------------------------------------------------
'
如果成功把newEngine設(shè)置為False
'------------------------------------------------------------------
newEngine = False
Catch e As
Exception
'------------------------------------------------------------------
'
加載失敗這是一個新類型引擎
'------------------------------------------------------------------
VsaEngines.Remove(strMoniker)
dtEngine = VsaEngines.Create(strMoniker, strLanguage,
Nothing)
engine = dtEngine
engine.InitNew()
'------------------------------------------------------------------
'
設(shè)置引擎名稱
'------------------------------------------------------------------
engine.Name =
strCustName
End Try
在主應(yīng)用程序中,有一個對dtClass 的接口中的屬性NewEngine 的簡單檢查。如果是一個新引擎,那么我們需要把參考和對象模型添加到引擎中;否則,就沒有事情要做,因為項目已經(jīng)被加載了。
把參考和對象模型添加到引擎中
這里我們有一個已經(jīng)準(zhǔn)備好為項目添加源代碼、對象模型和參考的DT引擎。主應(yīng)用程序使用的XML程序包含所有將被添加到VSA項目中的參考信息,因此這是需要添加到引擎中的第一樣?xùn)|西。把參考添加到設(shè)計時引擎與有腳本的.NET
Framework引擎十分相同。所有的引擎都執(zhí)行IVsa 。這只是用VsaItemType.Reference 的項目類型調(diào)用CreateItem
并把項目中的AssemblyName 設(shè)置為集合的路徑的一種方式。XML包含要加載的參考的名字和路徑,因此這就相當(dāng)簡單了。為了使這更簡單,類dtHost
通過方法AddReference提供了這樣的功能,它取得了名字和路徑。
'獲得參考
nodeList =
node.SelectNodes("reference")
For Each subNode In
nodeList
refName =
subNode.Attributes.ItemOf("name").Value
refAssembly =
subNode.Attributes.ItemOf("assembly").Value
myhost.AddReference(refName,
refAssembly)
Next
一旦所有的參考被添加盜引擎中,所有要去做的事情就是添加對象模型,腳本編寫者將用它來編程。向引擎中添加對象是通過調(diào)用引擎的一個代碼項目中的方法AddEventSource 而實(shí)現(xiàn)的。在例子中使用的XML中,所有的對象都是事件源對象,它們將被添加到類項目中。為了幫助這些動作的執(zhí)行,類dtHost 提供了一個創(chuàng)建和簡介事件源對象的代碼項目的抽象。
Dim nodeList As
XmlNodeList
nodeList = node.SelectNodes("class")
Dim classNode As
XmlNode
Dim stepit As Integer
Dim eventNodeList As XmlNodeList
stepit = 20 / nodeList.Count
For Each classNode In
nodeList
className =
classNode.Attributes.ItemOf("name").Value
myhost.AddClass(className)
eventNodeList =
classNode.SelectNodes("event")
Dim eventNode As XmlNode
For Each eventNode In
eventNodeList
eventName =
eventNode.Attributes.ItemOf("name").Value
eventType =
eventNode.Attributes.ItemOf("type").Value
myhost.AddEvent(className, eventName,
eventType)
Next
Me.ProgressBar1.Value = Me.ProgressBar1.Value +
stepit
Next
對象模型在服務(wù)器上的裝配和性能
在運(yùn)行服務(wù)器上的腳本時決定如果把對象模型添加到引擎中是很重要的,這里腳本代碼的速度和可測量性都是很重要的。一個VSA引擎可以用兩種方式添加對象:作為一個全局對象(不能激發(fā)事件)和作為一個事件源對象。全局對象令人驚訝的在腳本代碼的全局都有效,而結(jié)果是定義好不變的。靜態(tài)對象在一個單線程環(huán)境中運(yùn)行時是很好的,但是對于多線程環(huán)境的關(guān)注也是很重要的考慮。
在一個多線程的環(huán)境中,這樣的代碼在ASP.NET服務(wù)器中運(yùn)行,那里在任何時刻都潛在地會有許多單腳本的實(shí)例在運(yùn)行,并且用腳本聲明的靜態(tài)變量將被幾乎所有腳本所共享。因此,如果一個全局對象在腳本中,那么這個運(yùn)行的腳本的所有實(shí)例都將共享相同的對象實(shí)例。
為了說明這個潛在的問題,試想如果腳本中有一個名為“title”的簡單字符串屬性定義的全局對象。腳本的第一個實(shí)例把title屬性設(shè)置為“Hello World,”,并且會返回屬性的數(shù)值。在只有一個腳本實(shí)例在運(yùn)行時,這是很好的。現(xiàn)在再設(shè)想一個相同的情節(jié),但是這次是在設(shè)置屬性和返回數(shù)值之間,第二個引擎加載了一些但腳本稍微不同的相同對象模型。第二個腳本的第一行把title屬性設(shè)置為“hi from script 2”。由于在第一個腳本返回屬性的數(shù)值時,對象模型的實(shí)例被兩個腳本所共享,那么就會得到“hi from script 2”。兩個腳本在同一時刻都試圖去訪問屬性時,事情也許會變得更糟-不是很好的景象。
幸運(yùn)的是,這里有一種方法上下文隔離,來解決多多線程環(huán)境中的靜態(tài)變量的問題。上下文隔離確保靜態(tài)變量不能在所有實(shí)例中被共享,而Visual Studio for Applications 使用這種機(jī)制來確保你不會陷入我前面所描述的競爭問題。上下文隔離通過為每個線程創(chuàng)建靜態(tài)變量來管理這些。運(yùn)行在每個線程中的腳本代碼把靜態(tài)代碼看作與以前一樣,但是它看到的是為線程提供的上下文隔離的拷貝。
不幸的是,使用上下文隔離不是沒有代價的。為每個靜態(tài)變量創(chuàng)建一份拷貝,并且提供一個下部基礎(chǔ)來處理所有這些拷貝,招致要考慮性能的降低。在我們的性能測試站點(diǎn),我們看到與使用實(shí)例變量相比性能下降了50%。
為了避免使用靜態(tài)變量,并且避免他們的性能影響,看來要采用依靠服務(wù)器的方法了。由于創(chuàng)建在服務(wù)器上有效運(yùn)行的自定義的代碼是我們這版中的一員,因此幸運(yùn)的是這是我們已經(jīng)加到Visual Studio for Applications的東西。這里有兩種方法讓你可以把對象模型添加到得到靜態(tài)聲明變量的VSA:全局對象,和模型中包含的事件源對象。然而,添加到類項目中的事件源對象得到實(shí)例變量,它們將沒有任何性能問題。
如果你對模塊中的事件源對象必須是靜態(tài)的感到疑惑;原因就是在模塊中聲明的所有變量都是靜態(tài)的。因此-并且這將很有希望不會造成驚奇-我極力推薦你在類中給你為了在服務(wù)器上運(yùn)行腳本提供的全部對象模塊使用事件源對象。如果你使用靜態(tài)對象,那么VSA確保變量的什么包含ContextStatic 屬性,它告訴.NET創(chuàng)建上下文隔離,這樣就不會有腳本代碼陷入競爭的問題了。(這一定會比它需要的慢很多。)因此所有例子中的事件源對象都被添加到類中來給出一個很好的例子。
Me.ProgressBar1.Value =
90
myhost.InitCompleted()
Me.ProgressBar1.Value =
100
Button1.Visible = True
在使用引擎前,所有還需要做的事情是通過調(diào)用方法InitCompleted 來告訴它初始化已經(jīng)完成。
'
對引擎做的所有事情都已經(jīng)完成,因此調(diào)用InitCompleted
dtEngine.InitCompleted()
介紹IDE
現(xiàn)在VSA設(shè)計時引擎已經(jīng)得到所有所需的信息來允許開始編輯腳本,因此主應(yīng)用程序需要通知引擎使IDE可見。幸運(yùn)的是,這非常非常容易實(shí)現(xiàn),只要調(diào)用引擎中的方法ShowIDE
就可以了。為了使這個過程顯得有些活力,類dtHost 提供了一個方法Show 去調(diào)用方法ShowIDE
,而把它包含在一個catch塊中來俘獲任何意外并且為使用類dtHost
的應(yīng)用程序拋出適當(dāng)?shù)囊馔狻T陲@示了IDE之后,主應(yīng)用程序把自己最小化來使用戶可以看到VSA IDE。
'顯示VSA
IDE
myhost.Show()
'把自己最小化使用戶可以看到VSA
IDE
Me.WindowState = FormWindowState.Minimized
編寫腳本代碼
可以得到給一個應(yīng)用程序編寫腳本的全特性開發(fā)環(huán)境所需要的事情是你不再必須做你自己的(這通常是作為文本框的改變而結(jié)束。)。同樣,因為IDE為你提供了所有信息,而你也不用為可以得到哪些對象而擔(dān)心,因此編寫腳本是如此的簡單。
當(dāng)顯示調(diào)用把VSA IDE第一次顯示在用戶面前時,將向用戶介紹所有從XML添加的項目信息,而一個代碼窗口將顯示添加到項目中的類。
圖2. VSA IDE
顯示項目和類
為了編寫腳本代碼來自定義的計算對象,腳本編寫著者必須選擇他或她想要描述的事件。在VSA IDE中做這件事情是相當(dāng)簡單的;只是從代碼編輯器上面的對象列表中選擇calculateObjectModel,而接下來從事件列表中選擇事件。
圖 3. 對象列表框
圖4. 事件信息
選擇事件將造成一個事件句柄被自動添加到類中,腳本編寫者就可以用來編寫腳本了。
圖5. 所添加的事件句柄
由于VSA IDE給為VSA引擎提供的對象提供了完全的Microsoft? IntelliSense?支持,因此對應(yīng)用程序提供的對象模塊進(jìn)行訪問也是相當(dāng)簡單的。用戶只需要輸入對象的名稱和域,VSA IDE就會提供所有對象成員的列表。如果你已經(jīng)使用過Visual Basic for Applications (VBA),這對你來說不應(yīng)當(dāng)陌生,但是對于一個腳本用戶,這是一個主要步驟。
圖6.
calculateObjectModel的成員信息
保存代碼
一旦你滿意你編寫的代碼,你就需要能保存它。Visual Studio for
Applications 和.NET
Framework腳本的一個關(guān)鍵好處是用來運(yùn)行腳本的應(yīng)用程序可以確定這些代碼存儲在哪里。所有開發(fā)者都對點(diǎn)擊保存安鈕感到厭煩。當(dāng)你點(diǎn)擊保存,VSA
IDE將回調(diào)主應(yīng)用程序并且提供你已經(jīng)寫好的所有代碼,因此主應(yīng)用程序可以保存它。你的應(yīng)用程序選擇保存你的應(yīng)用程序代碼的地方對你完全是不隱瞞的。許多人選擇把它保存在數(shù)據(jù)庫中。為了保持事情的簡單,scripthappens.com
計算網(wǎng)絡(luò)服務(wù)把它的代碼存儲在磁盤上scripthappens vroot 的文件夾中。
VSA SDK關(guān)注大多數(shù)要保存代碼的下層結(jié)構(gòu),來用代碼管理器真正實(shí)現(xiàn)代碼的延續(xù)。你所有的代碼所需要做的是確定是保存為源代碼形式還是編譯后的形式。通常,你會希望保存編譯后的代碼,特別是當(dāng)編寫服務(wù)器自定義的時。在例子的代碼中,類dtHost 提供了一個先保存源代碼,編譯這個源代碼(只是在還沒有編譯時),最后再保存編譯后的狀態(tài)的方法SaveVsaEngine 。
Sub SaveVsaEngine()
Try
'------------------------------------------------------------------
' 保存源狀態(tài)
'------------------------------------------------------------------
SaveEngineSource(engine.RootMoniker, Nothing)
'------------------------------------------------------------------
'
編譯并保存自定義的代碼
'------------------------------------------------------------------
If engine.Compile()
Then
SaveEngineCompiledState(engine.RootMoniker,
Nothing)
End If
Catch e As
Exception
MsgBox("Error Saving Engine") ' &
e.StackTrace)
End Try
End Sub
當(dāng)方法SaveEngineSource 和 SaveEngineCompiledState 在類VSA SDK DT 中被調(diào)用時,這個類調(diào)用代碼管理器方法PutSourceCode 和 PutBinaryCode 依靠SOAP,來存儲代碼。
運(yùn)行代碼
已經(jīng)編寫了腳本代碼并且被你的代碼管理器保存到你代碼倉庫。所有接下來要做的就是運(yùn)行代碼并且看它如何工作了。
由于腳本代碼在服務(wù)器上運(yùn)行,確保它以正確的方式運(yùn)行就十分重要了。你的應(yīng)用程序?qū)⑦\(yùn)行腳本,所以VSA IDE依靠主應(yīng)用程序來確保腳本運(yùn)行得正確。XML程序定義在應(yīng)用程序部件中包含了一個targetURL 屬性來幫助解決這個問題。
例子中的dtHost 類使用targetURL 的數(shù)據(jù)來告訴VSA運(yùn)行時類在用戶要運(yùn)行或調(diào)試腳本時該啟動哪個URL。設(shè)計時類執(zhí)行IVSADTSite ,它在用戶運(yùn)行代碼并啟動URL時被VSA IDE回調(diào)。當(dāng)URL被啟動時,用戶輸入調(diào)用網(wǎng)絡(luò)服務(wù)所需的消息并且提交它。當(dāng)消息被提交給網(wǎng)絡(luò)服務(wù)時,網(wǎng)絡(luò)服務(wù)就將被實(shí)例化,然后加載已編譯的腳本代碼并運(yùn)行腳本代碼。
由于你不用做運(yùn)行代碼之外的其他事情,調(diào)試腳本就相當(dāng)自由了。與只是運(yùn)行腳本唯一不同的地方是腳本編寫者要選擇他們希望中斷和運(yùn)行代碼的行。運(yùn)行又造成URL被加載,而腳本在網(wǎng)絡(luò)服務(wù)被調(diào)用時被加載;當(dāng)腳本代碼運(yùn)行時,VSA IDE已經(jīng)準(zhǔn)備好在斷點(diǎn)中斷代碼。
圖7. 調(diào)試腳本代碼
存儲并找回腳本代碼
在本文的各處,我已經(jīng)提到VSA運(yùn)行時和設(shè)計時類似如何使用代碼管理器存儲和找回腳本代碼,但是我還沒有詳細(xì)介紹如何執(zhí)行代碼管理器。為了正確運(yùn)行代碼管理器,就需要完全的腳本診所(一個我將在以后做的東西)。然而,我將在這里復(fù)習(xí)一下基礎(chǔ)知識,這樣呢就可以知道例子中的代碼管理器是如何使用的了。
一個代碼管理器是一個.NET部件,它執(zhí)行ICodeProvider 接口并把名稱翻譯為應(yīng)用程序所使用的存儲形式。例如,一個代碼管理器會把名稱轉(zhuǎn)換為一系列SQL服務(wù)器或XML文檔中的查詢。這個接口被設(shè)計來允許加載,存儲并刪除源和二進(jìn)制代碼。設(shè)計將保持簡單和無國籍,這樣部件就可以在本地調(diào)用或作為網(wǎng)絡(luò)服務(wù)。
為了保持設(shè)置過程的簡單,這篇文章的例子中的代碼管理器巴所有腳本代碼保存到磁盤上。所有源和二進(jìn)制腳本都被存儲在網(wǎng)絡(luò)服務(wù)器中的scripthappens vroot 的vsa項目文件夾中。代碼管理器使用名稱中的應(yīng)用程序信息,并且在vsa項目文件夾中創(chuàng)建文件夾,因此com.scripthappens://calculate 最后成為vsa projectscalculate 。當(dāng)代碼管理器被從設(shè)計時主類中調(diào)用時,VSA項目,代碼項目,調(diào)試信息和已編譯的程序保存在文件夾中。
圖8.
scripthappens的代碼存儲
當(dāng)應(yīng)用程序運(yùn)行時,運(yùn)行時主類將使用代碼管理器來從calculate文件夾加載已編譯二進(jìn)制腳本。
總結(jié)
Visual Studio for Applications 和.NET
Framework腳本為你提供了一個很好的工具,來創(chuàng)建你或你的客戶可以進(jìn)行自定義的來滿足變化的需要的應(yīng)用程序。我希望這篇文章給你提供了如何創(chuàng)建可自定義的.NET應(yīng)用程序的很好的介紹。特別是,我希望你會知道怎樣使用VSA來創(chuàng)建可自定義的網(wǎng)絡(luò)服務(wù)并且提供從網(wǎng)絡(luò)瀏覽器對VSA
IDE的訪問。在這里,我將感謝Wayne King ,VSA
SDK組中的一個測試員,他把我的一些模糊白板上的設(shè)計概念如此好地應(yīng)用到例子中。那么,我們真的很希望聽到你的反饋,歡迎在VSA新聞組上與我們聯(lián)系,或者在msscript@microsoft.com 。
代碼門診部
Andrew Clinick 是一個Microsoft
Programmability組的資深的程序管理員,因此,如果涉及到腳本,他總會想辦法進(jìn)行處理。
- 1上海工程建設(shè)oa進(jìn)度管理軟件
- 2信息化技術(shù)是實(shí)現(xiàn)有效上海OA信息化的基礎(chǔ)
- 3Microsoft .NET XML Web服務(wù)
- 4樸素的解答-為何進(jìn)行上海OA信息化
- 5Web2.0與上海OA信息化系統(tǒng)
- 6kmpro上海OA信息化優(yōu)化理論
- 7從管人到集“知”:走進(jìn)上海OA信息化
- 8OA軟件不是你想象之中那樣
- 9知識的形成:人+信息+場景
- 10為什么對微軟的懼怕導(dǎo)致了Sun聯(lián)盟的出現(xiàn)
- 11上海三問投資控股集團(tuán)有限公司在線辦公OA系統(tǒng)
- 12上海OA信息化不神秘
- 13上海OA信息化咨詢服務(wù)的幾種模式
- 14泛普OA加速企業(yè)移動信息化普及
- 15上海OA信息化軟件的分類
- 16上海麗漢貿(mào)易有限公司OA辦公系統(tǒng)平臺
- 17建筑施工行業(yè)OA辦公軟件、項目管理軟件將是泛普軟件的“菜”
- 18Sun 擁有Java, 但是它的Web Service 在哪里?
- 19知識的分類與知識創(chuàng)新的過程
- 20OA辦公系統(tǒng)中,流程表單的填寫和發(fā)送、計劃日程的安排和整理
- 21經(jīng)營知識
- 22建立共享的企業(yè)文化
- 23杭州市城市建設(shè)檔案管理辦法(試行)
- 24Sun推出網(wǎng)絡(luò)服務(wù)軟件與微軟一爭高低
- 25KM和OA之異同
- 26導(dǎo)師制與上海OA信息化
- 27組織創(chuàng)新中上海OA信息化內(nèi)容
- 28OA協(xié)同系統(tǒng)的啟用與完善,將加快實(shí)現(xiàn)特檢院辦公先進(jìn)性
- 29上海保集(集團(tuán))有限公司OA辦公系統(tǒng)平臺
- 30推進(jìn)行政管理辦公自動化(OA)對于加強(qiáng)高校自身建設(shè)
成都公司:成都市成華區(qū)建設(shè)南路160號1層9號
重慶公司:重慶市江北區(qū)紅旗河溝華創(chuàng)商務(wù)大廈18樓
泛普上海OA信息化其他應(yīng)用
上海OA軟件 上海OA新聞動態(tài) 上海OA信息化 上海OA快博 上海OA軟件行業(yè)資訊 上海軟件開發(fā)公司 上海門禁系統(tǒng) 上海物業(yè)管理軟件 上海倉庫管理軟件 上海餐飲管理軟件 上海網(wǎng)站建設(shè)公司
版權(quán)所有:泛普軟件 渝ICP備14008431號-2 渝公網(wǎng)安備50011202501700號 咨詢電話:400-8352-114