版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1/1模型-視圖-提供器模式引言
隨著像http://./doc/7dcae875a417866fb84a8eed.html和Windows窗體這樣的用戶(hù)界面創(chuàng)建技術(shù)越來(lái)越強(qiáng)大,讓用戶(hù)界面層做多于它本應(yīng)做的事是很常見(jiàn)的。沒(méi)有一個(gè)清晰的職責(zé)劃分,UI層經(jīng)常淪為一個(gè)包含實(shí)際上應(yīng)屬于程序其他層的邏輯的容器。有一個(gè)稱(chēng)為模型(Model)-視圖(View)-提供器(Presenter)(MVP)的設(shè)計(jì)模式,特別適合解決這個(gè)問(wèn)題。為了表明我的觀點(diǎn),我將為Northwind數(shù)據(jù)庫(kù)中的客戶(hù)建一個(gè)遵循MVP模式的顯示屏幕(displayscreen)。
為什么在UI層包含太多的邏輯是很糟糕的?在既不手動(dòng)運(yùn)行應(yīng)用程序,也不維護(hù)丑陋的自動(dòng)執(zhí)行UI組件的UI運(yùn)行者腳本(runnerscript)的情況下,位于應(yīng)用程序UI層中的代碼是非常難于調(diào)試的。雖然這本身就是一個(gè)很大的問(wèn)題,一個(gè)更大的問(wèn)題是在應(yīng)用程序的公共視圖之間會(huì)有大量的重復(fù)代碼。當(dāng)執(zhí)行某一特定業(yè)務(wù)的功能在UI層的不同部分之間拷貝,通常很難找到好的可選重構(gòu)方法。MVP設(shè)計(jì)模式使得將UI層中的邏輯和代碼重構(gòu)為更加易于測(cè)試的新型的、可重用的代碼更加容易。
圖1演示了組成一個(gè)范例應(yīng)用程序的主要層。注意對(duì)于UI和表現(xiàn)(Pesentation)有著各自的包(Package)。你可能會(huì)想它們是一樣的,但是實(shí)際上項(xiàng)目中的UI層應(yīng)該只包含各種不同的UI元素――窗體和控件。典型地,在一個(gè)Web窗體項(xiàng)目中是http://./doc/7dcae875a417866fb84a8eed.htmlWeb窗體、用戶(hù)控件、服務(wù)器控件的集合;在Windows項(xiàng)目中,它是Windows窗體、用戶(hù)控件以及第三方庫(kù)(Libraries)的集合。這一額外的層就是將顯示和邏輯分隔開(kāi)的層。在表現(xiàn)層,你擁有實(shí)際上實(shí)現(xiàn)UI行為的對(duì)象――諸如驗(yàn)證顯示,從UI層收集用戶(hù)輸入等等。
圖1.應(yīng)用程序構(gòu)架
遵循MVP
如同你在圖2中所見(jiàn)的,這個(gè)項(xiàng)目的UI相當(dāng)標(biāo)準(zhǔn)。當(dāng)頁(yè)面加載時(shí),屏幕將會(huì)顯示一個(gè)包含Northwind數(shù)據(jù)庫(kù)中所有客戶(hù)的下拉框。如果你從下拉框中選擇一個(gè)客戶(hù),頁(yè)面會(huì)更新為這個(gè)客戶(hù)的信息。通過(guò)遵循MVP設(shè)計(jì)模式,你可以從UI中將行為(Behavior)重構(gòu)到它們自己的類(lèi)中。圖3顯示了一個(gè)類(lèi)圖,它說(shuō)明了參與其中的各個(gè)不同類(lèi)之間的聯(lián)系。
圖2.用戶(hù)信息
圖3.MVP類(lèi)圖
注意到提供器對(duì)于應(yīng)用程序?qū)嶋H的UI層一無(wú)所知非常重要。它知道它可以同接口對(duì)話,但是它不知道也不關(guān)心接口的實(shí)現(xiàn)是什么。這提升了在完全不同的UI技術(shù)間提供器的重用。
我將使用測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(TDD)創(chuàng)建客戶(hù)界面的功能。代碼4演示了第一次測(cè)試的細(xì)節(jié),我將通過(guò)這個(gè)測(cè)試來(lái)描述我期望在頁(yè)面加載時(shí)觀察到的行為。TDD讓我每次關(guān)注于一個(gè)問(wèn)題,
僅編寫(xiě)可以讓測(cè)試通過(guò)的代碼,然后繼續(xù)進(jìn)行下面的工作。在測(cè)試中,我將會(huì)利用一個(gè)稱(chēng)為NMork2的偽對(duì)象框架,它允許我創(chuàng)建接口的偽實(shí)現(xiàn)(mockimplementation)。
代碼4.第一個(gè)測(cè)試
[Test]
publicvoidShouldLoadListOfCustomersOnInitialize()
{
mockery=newMockery();
ICustomerTaskmockCustomerTask=mockery.NewMock();
IViewCustomerViewmockViewCustomerView=
mockery.NewMock();
ILookupListmockCustomerLookupList=mockery.NewMock();
ViewCustomerPresenterpresenter=
newViewCustomerPresenter(mockViewCustomerView,mockCustomerTask);
ILookupCollectionmockLookupCollection=
mockery.NewMock();
Expect.Once.On(mockCustomerTask).Method(
"GetCustomerList").Will(Return.Value(mockLookupCollection));
Expect.Once.On(mockViewCustomerView).GetProperty(
"CustomerList").Will(Return.Value(mockCustomerLookupList));
Expect.Once.On(mockLookupCollection).Method(
"BindTo").With(mockCustomerLookupList);
presenter.Initialize();
}
在我的MVP實(shí)現(xiàn)中,我決定提供器將作為視圖所要與之工作的依賴(lài)。通常,創(chuàng)建對(duì)象使之處于可以立刻進(jìn)行工作的狀態(tài)是一種好的做法。在這個(gè)應(yīng)用程序中,表現(xiàn)層依賴(lài)于服務(wù)層,實(shí)際上由服務(wù)層調(diào)用領(lǐng)域功能(domainfunctionality)。因?yàn)檫@個(gè)需求,創(chuàng)建一個(gè)含有可以與服務(wù)類(lèi)對(duì)話的接口的提供器也是有意義的。這樣確保了一旦提供器創(chuàng)建好了,它就已經(jīng)準(zhǔn)備好做它需要做的所有工作了。我以創(chuàng)建兩個(gè)特定的mocks作為開(kāi)始:一個(gè)用于服務(wù)層,一個(gè)用于提供器將與協(xié)作的視圖。
為什么使用mocks?單元測(cè)試的一個(gè)規(guī)則就是盡可能地隔離測(cè)試以便集中于某一特定的對(duì)象。在這個(gè)測(cè)試中,我只關(guān)心提供器所期待的行為。目前我并不關(guān)心view接口或者service接口的實(shí)際實(shí)現(xiàn)。我信任由這些接口定義的契約(contract),并且設(shè)置mocks去相應(yīng)運(yùn)作(behave)。這樣確保了我的測(cè)試僅僅圍繞著我對(duì)提供器所期望的行為,而不是它所依賴(lài)的任何東西。我期望的,在提供器的初始化方法被調(diào)用后所表現(xiàn)的行為如下:首先,提供器應(yīng)該調(diào)用一次服務(wù)層ICustomerTask對(duì)象(已經(jīng)在測(cè)試中Mock了)的GetCustomerList方法。注意通過(guò)使用NMock,我可以模擬Mock的行為。以服務(wù)層來(lái)說(shuō),我想要返回一個(gè)ILookupCollection給提供器。然后,在提供器從服務(wù)層收到ILookupCollection以后,它可以調(diào)用集合的BindTo方法并且向方法傳遞一個(gè)ILookupList方法的實(shí)現(xiàn)。通過(guò)使用NMockExpect。一旦我可以確定方法,如果提供器沒(méi)有調(diào)用這個(gè)方法一次并且只一次,那么測(cè)試將會(huì)失敗。
寫(xiě)完測(cè)試以后,我處于一個(gè)完全不可編譯的狀態(tài)。我將要做一些可能的最簡(jiǎn)單的事讓測(cè)試通過(guò)。
讓第一個(gè)測(cè)試通過(guò)
先寫(xiě)一個(gè)測(cè)試的好處之一是我現(xiàn)在有了一個(gè)我可以遵循的使得測(cè)試編譯并最終通過(guò)的藍(lán)圖(這個(gè)測(cè)試)。第一個(gè)測(cè)試還有兩個(gè)尚不存在的接口。這些接口是代碼正確通過(guò)編譯的第一個(gè)先決條件。我們將以IViewCustomerView的代碼作為開(kāi)始:
publicinterfaceIViewCustomerView{
ILookupListCustomerList{get;}
}
這個(gè)接口暴露一個(gè)返回ILookupList接口實(shí)現(xiàn)的屬性。我還沒(méi)有ILookupList接口或是它的一個(gè)實(shí)現(xiàn),就此而言。出于使測(cè)試通過(guò)的目的,我不需要一個(gè)顯示的實(shí)現(xiàn),所以我可以這樣去創(chuàng)建ILookupList接口:
publicinterfaceILookupList{}
ILookupList接口現(xiàn)在看上去相當(dāng)?shù)臎](méi)用。我的目標(biāo)是使得測(cè)試編譯并且通過(guò),并且這些接口滿足測(cè)試的需要?,F(xiàn)在是時(shí)候?qū)⒔裹c(diǎn)轉(zhuǎn)移到我們實(shí)際要進(jìn)行測(cè)試的對(duì)象上了――ViewCustomerPresenter。這個(gè)類(lèi)現(xiàn)在還不存在,但是看下測(cè)試,你可以發(fā)現(xiàn)關(guān)于它的兩個(gè)要點(diǎn):它有一個(gè)既需要視圖實(shí)現(xiàn)也需要服務(wù)實(shí)現(xiàn)作為依賴(lài)的構(gòu)造函數(shù),并且它有一個(gè)無(wú)返回值的初始化方法。代碼5演示了如何使測(cè)試通過(guò)編譯:
代碼5.編譯這個(gè)測(cè)試
publicclassViewCustomerPresenter
{
privatereadonlyIViewCustomerViewview;
privatereadonlyICustomerTasktask;
publicViewCustomerPresenter(
IViewCustomerViewview,ICustomerTasktask)
{
this.view=view;
this.task=task;
}
publicvoidInitialize()
{
thrownewNotImplementedException();
}
}
應(yīng)該記得,為了讓提供器有意義地工作,它需要獲得它的所有依賴(lài);這就是為什么傳遞視圖和服務(wù)進(jìn)去。我沒(méi)有實(shí)現(xiàn)初始化方法,所以如果我運(yùn)行這個(gè)測(cè)試我會(huì)得到一個(gè)NotImplementedException異常。
如果我已經(jīng)提到的,我不會(huì)盲目地對(duì)提供器進(jìn)行編碼;我已經(jīng)知道,通過(guò)觀察這個(gè)測(cè)試,在初始化方法被調(diào)用時(shí),提供器應(yīng)該顯示出什么樣的行為。這個(gè)行為的實(shí)現(xiàn)如下所示:publicvoidInitialize(){
task.GetCustomerList().BindTo(view.CustomerList);
}
在這篇文章所附帶的源代碼中,在CustomerTask類(lèi)(它實(shí)現(xiàn)了ICustomerTask接口)中有GetCustomerList方法的完整實(shí)現(xiàn)。然而,從實(shí)現(xiàn)和測(cè)試提供器的角度來(lái)說(shuō),我不需要知道是否有一個(gè)可以工作的實(shí)現(xiàn)。正是這種級(jí)別的抽象允許我在提供器類(lèi)的測(cè)試中穿行。第一個(gè)測(cè)試現(xiàn)在處于可以編譯并運(yùn)行的狀態(tài)。這證明了當(dāng)提供器的初始化方法被調(diào)用,它將會(huì)以一種我在測(cè)試中所指定的方式與它所依賴(lài)的類(lèi)型進(jìn)行交互,并且最終,當(dāng)這些依賴(lài)的具體實(shí)現(xiàn)注入到提供器中,我可以確定結(jié)果視圖(ASPX頁(yè)面)將會(huì)由客戶(hù)列表所填充。
填充DropDownList
迄今為止,我主要在處理接口以便將實(shí)際的實(shí)現(xiàn)細(xì)節(jié)抽象出來(lái)、將注意力集中在提供器上?,F(xiàn)在是時(shí)候通過(guò)一種可測(cè)試的方式創(chuàng)建一些底層代碼(plumbing),這些底層代碼將最終允許提供器在Web頁(yè)面上填充一個(gè)列表。完成這個(gè)工作的關(guān)鍵是將發(fā)生在LookupCollection類(lèi)的BindTo方法中的交互。如果你看下代碼6中LookupCollection
類(lèi)的實(shí)現(xiàn),你將注意到它實(shí)現(xiàn)了IlookupCollection接口。這篇文章的源碼含有附帶的測(cè)試,用于創(chuàng)建LookupCollection類(lèi)的功能。
代碼6.LookupCollection類(lèi)
publicclassLookupCollection:ILookupCollection
{
privateIListitems)
{
this.items=newList(items);
}
publicintCount{get{returnitems.Count;}}
publicvoidBindTo(ILookupListlist)
{
list.Clear();
foreach(ILookupDTOdtoinitems)list.Add(dto);
}
}
BindTo方法的實(shí)現(xiàn)值得特別注意。注意到在這個(gè)方法中,集合遍歷了它自己的私有ILookupDTO列表的實(shí)現(xiàn)。ILookupDTO是一個(gè)接口,它迎合了UI層的綁定下拉框。
publicinterfaceILookupDTO{
stringValue{get;}
stringText{get;}
}
代碼7演示了測(cè)試lookup集合的BindTo方法的代碼,這有助于解釋LookupCollection和IlookupList之間所期望的交互。最后一行值得特別注意。在這個(gè)測(cè)試中,我期望在試圖添加項(xiàng)目到列表之前,LookupCollection將會(huì)調(diào)用IlookupList實(shí)現(xiàn)的Clear方法。然后我期望Add方法在IlookupList上調(diào)用10次,并且LookupCollection將傳遞一個(gè)實(shí)現(xiàn)了ILookupDTO接口的對(duì)象,作為Add方法的一個(gè)參數(shù)。為了能夠?qū)嶋H工作在一個(gè)Web項(xiàng)目中
的控件上(比如一個(gè)下拉列表),你將需要?jiǎng)?chuàng)建一個(gè)IlookupList的實(shí)現(xiàn),它知道如何與Web項(xiàng)目中的控件工作。
代碼7一個(gè)描述行為的測(cè)試
[Test]
publicvoidShouldBeAbleToBindToLookupList()
{
IListdtos=newIList;
ILookupListmockLookupList=mockery.NewMock();
Expect.Once.On(mockLookupList).Method("Clear");
for(inti=0;i
這些已經(jīng)就位了,我們可以立刻繼續(xù)去實(shí)現(xiàn)滿足IViewCustomerView接口實(shí)現(xiàn)的代碼了:
publicILookupListCustomerList{
get{returnnewWebLookupList(this.customerDropDownList);}}
我現(xiàn)在需要在提供器上調(diào)用初始化方法,這個(gè)方法將觸發(fā)它去做些實(shí)際的工作。為了完成這個(gè),視圖需要能夠初始化提供器,以便它的方法可以被調(diào)用。如果你回頭看下提供器,你將會(huì)記得它需要與視圖和服務(wù)工作。ICustomerTask代表一個(gè)居于應(yīng)用程序服務(wù)層中的一個(gè)接口。典型地服務(wù)層負(fù)責(zé)監(jiān)管領(lǐng)域?qū)ο箝g的交互,以及將這些交互的結(jié)果轉(zhuǎn)換成數(shù)據(jù)傳遞對(duì)象(DTOs),然后這些DTO對(duì)象從服務(wù)層傳遞到表現(xiàn)層,接著傳遞到UI層。然而,這里有一個(gè)問(wèn)題,我規(guī)定提供器需要視圖和服務(wù)的實(shí)現(xiàn)才能創(chuàng)建。
提供器實(shí)際的初始化將發(fā)生在Web頁(yè)面的后置代碼中。這是一個(gè)問(wèn)題,因?yàn)閁I項(xiàng)目不包含對(duì)服務(wù)層項(xiàng)目的引用。然而,表現(xiàn)層項(xiàng)目包含,它有一個(gè)對(duì)服務(wù)層項(xiàng)目的引用。這允許我通過(guò)在ViewCustomverPresenter中添加一個(gè)重載的構(gòu)造函數(shù)來(lái)解決這個(gè)問(wèn)題:publicViewCustomerPresenter(IViewCustomerViewview):this(view,new
CustomerTask()){}
新的構(gòu)造函數(shù)滿足了提供器的需求:同時(shí)擁有視圖和服務(wù)的實(shí)現(xiàn),并且保持將UI層從服務(wù)層中分離出來(lái)?,F(xiàn)在完成后置代碼是很輕易的事情了:
protectedoverridevoidOnInit(EventArgse){
base.OnInit(e);
presenter=newViewCustomerPresenter(this);
}
protectedvoidPage_Load(objectsender,EventArgse){
if(!IsPostBack)presenter.Initialize();
}
注意到初始化提供器的關(guān)鍵是:我利用了我新創(chuàng)建的重載構(gòu)造函數(shù),并且Web窗體將它本身作為一個(gè)實(shí)現(xiàn)了View接口的對(duì)象進(jìn)行傳遞!
后置代碼已經(jīng)實(shí)現(xiàn),現(xiàn)在我可以生成并運(yùn)行應(yīng)用程序了。Web頁(yè)面上的DropDownList現(xiàn)在填充了客戶(hù)名稱(chēng)列表,而在后置代碼中不需要任何的數(shù)據(jù)綁定代碼。不僅如此,曾經(jīng)運(yùn)行的各個(gè)小部分的測(cè)試最終協(xié)同工作了,確保了表現(xiàn)層構(gòu)架將會(huì)如期望般運(yùn)作。
我將通過(guò)演示顯示一個(gè)在DropDownList中選中的客戶(hù)信息,把我關(guān)于MVP的討論聯(lián)系起來(lái)。再一次,我通過(guò)寫(xiě)一個(gè)描述了我希望觀察到的行為的測(cè)試作為開(kāi)始(看代碼11)。
代碼11.最后一個(gè)測(cè)試
[Test]
publicvoidShouldDisplayCustomerDetails()
{
SimpleLookupDTOlookupDTO=newSimpleLookupDTO("1","JPBOO");
CustomerDTOdto=newCustomerDTO("BLAH","BLAHCOMPNAME",
"BLAHCONTACTNAME","BLAHCONTACTTILE","ADDRESS","CITY",
"REGION","POSTALCODE",Country.CANADA,"4444444","4444444");
Expect.Once.On(mockViewCustomerView).GetProperty(
"CustomerList").Will(Return.Value(mockCustomerLookupList));
Expect.Once.On(mockCustomerLookupList).GetProperty(
"SelectedItem").Will(Return.Value(lookupDTO));
Expect.Once.On(mockCustomerTask).Method(
"GetDetailsForCustomer").With(1).Will(Return.Value(dto));
Expect.Once.On(mockViewCustomerView).SetProperty(
"CompanyName").To(http://./doc/7dcae875a417866fb84a8eed.htmlpanyName);
Expect.Once.On(mockViewCustomerView).SetProperty(
"ContactName").To(dto.ContactName);
Expect.Once.On(mockViewCustomerView).SetProperty(
"ContactTitle").To(dto.ContactTitle);
Expect.Once.On(mockViewCustomerView).SetProperty(
"Address").To(dto.Address);
Expect.Once.On(mockViewCustomerView).SetProperty(
"City").To(dto.City);
Expect.Once.On(mockViewCustomerView).SetProperty(
"Region").To(dto.Region);
Expect.Once.On(mockViewCustomerView).SetProperty(
"PostalCode").To(dto.PostalCode);
Expect.Once.On(mockViewCustomerView).SetProperty(
"Country").To(http://./doc/7dcae875a417866fb84a8eed.html);
Expect.Once.On(mockViewCustomerView).SetProperty(
"Phone").To(dto.Phone);
Expect.Once.On(mockViewCustomerView).SetProperty("Fax").To(dto.Fax);
presenter.DisplayCustomerDetails();
}
和前面一樣,我利用NMock庫(kù)創(chuàng)建task和View接口的Mocks。這個(gè)特定的測(cè)試通過(guò)向服務(wù)層請(qǐng)求一個(gè)代表某一特定客戶(hù)的DTO,驗(yàn)證了提供器的行為。一旦提供器從服務(wù)層獲得DTO,它將直接更新視圖的屬性,這就避免了視圖需要知道如何正確地顯示來(lái)自對(duì)象的信息。
為了簡(jiǎn)潔,我不打算去討論WebLookupList控件的SeletedItem屬性的實(shí)現(xiàn);然而,我將把它留給你,通過(guò)檢查源代碼來(lái)查看實(shí)現(xiàn)的細(xì)節(jié)。這個(gè)測(cè)試真正演示的是當(dāng)提供器從服務(wù)層收到一個(gè)CustomerDTO時(shí)發(fā)生在提供器和視圖之間的交互。如果我現(xiàn)在試圖運(yùn)行這個(gè)測(cè)試,我將會(huì)處于一個(gè)嚴(yán)重的錯(cuò)誤狀態(tài),因?yàn)楹芏嗟膶傩詖iew接口沒(méi)有定義。所以我將繼續(xù)為IViewCustomerView接口添加必要的成員,如同你在代碼12看到的:
代碼12.完成IViewCustomerView接口
publicinterfaceIViewCustomerView
{
ILookupListCustomerList{get;}
stringCompanyName{set;}
stringContactName{set;}
stringContactTitle{set;}
stringAddress{set;}
stringCity{set;}
stringRegion{set;}
stringPostalCode{set;}
stringCountry{set;}
stringPhone{set;}
stringFax{set;}
}
剛添加完這些接口成員,我的Web窗體就開(kāi)始抱怨了,因?yàn)樗辉贊M足接口的定義,所以我不得不回頭看下我的Web窗體的后置代碼,并且實(shí)現(xiàn)那些剩下的成員。如同前面所陳述的,Web頁(yè)面的整個(gè)標(biāo)記都已經(jīng)創(chuàng)建了,并讓那些標(biāo)記了“runat=server”的表格單元格根據(jù)將在它中所要顯示的信息來(lái)為它命名。這將使實(shí)現(xiàn)接口成員的代碼非常的輕易:publicstringCompanyName{
set{http://./doc/7dcae875a417866fb84a8eed.htmlpanyNameLabel.InnerText=value;}
}
publicstringContactName{
set{this.contactNameLabel.InnerText=value;}
}
...
實(shí)現(xiàn)了Set屬性訪問(wèn)器,還剩下一件事需要做。我需要有一種方式通知提供器,以便顯示選中客戶(hù)的信息?;仡^看下測(cè)試,你可以看到這個(gè)行為的實(shí)現(xiàn)位于提供器的DisplayCustomerDetails方法上。然而,這個(gè)方法不會(huì)接受任何參數(shù)。當(dāng)調(diào)用時(shí),提供器將會(huì)回頭找視圖,從它中拖出任何所需要的信息(它通過(guò)使用ILookupList獲取),然后使用這些信息獲取所請(qǐng)求的客戶(hù)的詳細(xì)內(nèi)容。從UI的角度來(lái)看,我需要做的全部就是將DropDownList的AutoPostBack屬性設(shè)為T(mén)rue,我也需要添加下面的事件處理程序,和Page的OnInit方法掛接起來(lái)。
protectedoverridevoidOnInit(EventArgse)
{
base.OnInit(e);
presenter=newViewCustomerPresenter(this);
this.customerDropDownList.SelectedIndexChanged+=delegate{
presenter.DisplayCustomerDetails();
};
}
這個(gè)事件處理程序確保,無(wú)論什么時(shí)候下拉框中的一個(gè)新的客戶(hù)被選中,視圖將會(huì)請(qǐng)求提供器顯示客戶(hù)的細(xì)節(jié)。
注意到這是一個(gè)典型的行為很重要。當(dāng)一個(gè)視圖請(qǐng)求提供器做一些事情,它不提供任何的特定細(xì)節(jié),而是由提供器去訪問(wèn)視圖,通過(guò)view接口獲取它所需要的任何信息。代碼13顯示了實(shí)現(xiàn)提供器的行為所需要的代碼。
代碼13完成提供器
publicvoidDisplayCustomerDetails(){
int?customerId=SelectedCustomerId;
if(customerId.HasValue)
{
CustomerDTOcustomer=
task.GetDetailsForCustomer(customerId.Value);
UpdateViewFrom(customer);
}
}
privateint?SelectedCustomerId{
get{
stringselectedId=view.CustomerList.SelectedItem.Value;
if(String.IsNullOrEmpty(selectedId))returnnull;
int?id=null;
try{
id=int.Parse(selectedId.Trim());
}
catch(FormatException){}
returnid;
}
}
privatevoidUpdateViewFrom(CustomerDTOcustomer){
htt
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025中國(guó)重癥心血管疾病臨床營(yíng)養(yǎng)評(píng)估與管理專(zhuān)家共識(shí)課件
- 《機(jī)械能》教案物理科課件
- 十九屆四中精神應(yīng)知應(yīng)會(huì)試題
- 車(chē)隊(duì)五一節(jié)前安全培訓(xùn)課件
- 影像技師年度影像設(shè)備日常維護(hù)與性能校準(zhǔn)工作總結(jié)(3篇)
- 2025年門(mén)診醫(yī)生就診效率提升與患者就醫(yī)體驗(yàn)改善專(zhuān)項(xiàng)總結(jié)(3篇)
- 銀行內(nèi)部調(diào)查與處理制度
- 銀行合規(guī)管理制度更新
- 2026年工地員工考試題目及答案
- 車(chē)間檢修前安全培訓(xùn)報(bào)道課件
- 君山島年度營(yíng)銷(xiāo)規(guī)劃
- 2025年山東師范大學(xué)馬克思主義基本原理概論期末考試參考題庫(kù)
- 期末測(cè)試卷(試卷)2025-2026學(xué)年三年級(jí)數(shù)學(xué)上冊(cè)(人教版)
- 2025年福建江夏學(xué)院毛澤東思想和中國(guó)特色社會(huì)主義理論體系概論期末考試模擬題及答案1套
- DB32T 5132.3-2025 重點(diǎn)人群職業(yè)健康保護(hù)行動(dòng)指南 第3部分:醫(yī)療衛(wèi)生人員
- 2025秋中國(guó)南水北調(diào)集團(tuán)新能源投資有限公司校園招聘(25人)(公共基礎(chǔ)知識(shí))測(cè)試題帶答案解析
- 2025至2030中國(guó)X射線衍射儀(XRD)行業(yè)產(chǎn)業(yè)運(yùn)行態(tài)勢(shì)及投資規(guī)劃深度研究報(bào)告
- 核電廠抗震設(shè)計(jì)標(biāo)準(zhǔn)
- 2026年經(jīng)銷(xiāo)商合同
- 2023-2025年中考英語(yǔ)真題匯編01之單項(xiàng)選擇(時(shí)態(tài)和語(yǔ)態(tài))
- 學(xué)堂在線 雨課堂 學(xué)堂云 科研倫理與學(xué)術(shù)規(guī)范 章節(jié)測(cè)試答案
評(píng)論
0/150
提交評(píng)論