日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

使用ASP.NET实现Model View Presenter(MVP)

發布時間:2025/5/22 asp.net 64 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用ASP.NET实现Model View Presenter(MVP) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者:Billy McCafferty 翻譯:張善友<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

原文地址:http://www.codeproject.com/useritems/ModelViewPresenter.asp

?

這篇文章描述了ASP.NET 2.0使用Model-View-Presenter 模式實現業務邏輯與表現層的適當分離。

  • Download trivial example of MVP - 18 Kb

  • Download simple Event-Handling MVP - 19 Kb

  • Download sample MVP Enterprise Solution - 2.6 Mb

概述

?? 經過多年代的ASP代碼積累,微軟開發了具有一流水平的網絡平臺:ASP.NET. ASP.NET使用后置代碼頁面方式隔離業務邏輯。雖然用心良苦,但是ASP.NET在企業級應用開發方面還是存在如下的不足:

l??????? 后置代碼頁中混合了表現層,業務邏輯層,數據訪問層的代碼。之所以出現這種情況是因為后置代碼充當了事件引發,流程控制,業務規則和表現邏輯,業務邏輯和數據訪問的協調者等多種角色。后置代碼頁充當這么多的職責導致許多難處理的代碼。在企業應用中,一個良好的設計原則是各層之間的適當分離和保持后置代碼頁內容的盡可能干凈。使用Model-View-Presenter 模式,后置代碼的內容將非常簡單,嚴格的管理表現層內容。

l??????? 后置代碼模型的另一個缺點是它難以不借助幫助類/工具類實現重用后置代碼頁面之間的可重用代碼。很明顯的,這也是提供了一個適當的解決方案,但往往導致ASP式的類,不像是一流的對象。通過適當的設計,每個類都應有清晰的職責,通常一個叫

ContainsDuplicatePresentationCodeBetweenThisAndThat.cs并不合適

l??????? 最后,對后置代碼頁進行單元測試非常困難因為它們同表現層的太緊密了,當然可以選擇NUnitASP這樣的工具,但是他們非常的耗費時間,并且難以維護。單元測試應當是簡單快速的。

?

? 可以采用各種技術手段是后置代碼頁保持分離。例如Castle MonoRail項目仿效Ruby-On-Rails ,但是放棄了ASP.NET的事件模型。Maverick.NET是一個支持ASP.NET事件模型的框架但是保留后置代碼頁作為程序的控制器。理想的解決方案是使用ASP.NET的事件模型并保持后置代碼頁的盡可能簡單。Model-View-Presenter 模式是一個不需要借助第三方框架實現這個目標。

?

Model-View-Presenter

Model-View-Presenter (MVP) 模式是 Model-View-Controller (MVC) 模式的變種,針對事件模型,像ASP.NET這樣的框架。具體參看簡單介紹GUI設計模式(MVPMVP最初使用與Dolphin Smalltalk. 主要的變化是Presenter實現MVC的Observer設計,基本設計和MVC相同:Model存儲數據,View表示Model的表現,Presenter協調兩者之間的通信。在 MVP 中 view 接收到事件,然后會將它們傳遞到 Presenter, 如何具體處理這些事件,將由 Presenter 來完成。關于在 MVC 和 MVP的深入比較,請查看

http://www.darronschall.com/weblog/archives/000113.cfm.,接下來以三個例子詳細說明MVP模式。

?

最簡單的例子

?? 這個例子,客戶想在頁面上顯示當前的時間(從簡單的開始容易理解)。顯示時間的ASPX頁面是“View”。Presenter負責決定現在的時間(Model,而且把Model告知 View。我們從一個單元測試開始。

?

[TestFixture]

public class CurrentTimePresenterTests {

??? [Test]

??? public void TestInitView() {

??????? MockCurrentTimeView view = new MockCurrentTimeView();

??????? CurrentTimePresenter presenter = new CurrentTimePresenter(view);

??????? presenter.InitView();

?

??????? Assert.IsTrue(view.CurrentTime > DateTime.MinValue);

??? }

?

??? private class MockCurrentTimeView : ICurrentTimeView {

???? ???public DateTime CurrentTime {

??????????? set { currentTime = value; }

?

??????????? // This getter won't be required by ICurrentTimeView,

??????????? // but it allows us to unit test its value.

??????????? get { return currentTime; }

??????? }

?

??????? private DateTime currentTime = DateTime.MinValue;

??? }

}

上面的單元測試代碼和右邊的類圖,描述了MVP各個元素之間的關系。單元測試中創建的第一個對象實例是MockCurrentTimeView,從這個單元測試中可以看出,所有的表現邏輯的單元測試并沒有一個ASPX頁面(View),所需要的是一個實現視圖接口的對象;因此可以創建一個視圖的模擬對象(Mockview)代替真正的視圖對象。

下一行代碼創建了一個Presenter的對象實例,通過它的構造函數傳遞了一個實現ICurrentTimeView接口的對象,這樣,Presenter現在能夠操作View,從類圖中可以看出,Presenter只與View的接口通信。這允許實現相同的View接口的多個View被Presenter使用。

最后,Presenter調用InitView()方法,這個方法將獲取當前的時間并通過公開的屬性ICurrentTimeView傳遞給視圖(View),單元測試斷言CurrentTime的值應比它的初始值大(如果需要可以做更多的斷言)。

那么現在要做的就是要運行單位測試并通過了!

?

ICurrentTimeView.cs 視圖接口

使單元測試編譯通過的第一步是創建ICurrentTimeView.cs,這個接口提供Presenter 和 View之間的溝通橋梁,在這個例子中,視圖接口需要暴露一個Model數據,使Persenter能夠將Model(當前時間)傳遞給View。

public interface ICurrentTimeView {

??? DateTime CurrentTime { set; }

}

因為只需要顯示模型數據,視圖接口中只需要一個CuttentTime的Set;但是設置了一個Get,用于在單元測試中獲取視圖的CurrentTime,它也可以添加到MockCurrentTimeView而不要在接口中定義,這樣,在視圖接口中暴露的接口屬性不需要定義getter/setter(上面的單元測試就使用了這個技術)。

?

CurrentTimePresenter.cs - The Presenter

Presenter處理同Model之間的邏輯并將Model傳遞給View。要使單元測試通過編譯,Presenter的實現代碼如下:

public class CurrentTimePresenter {

??? public CurrentTimePresenter(ICurrentTimeView view) {

??????? if (view == null) throw new ArgumentNullException("view may not be null");

?

??????? this.view = view;

??? }

?

??? public void InitView() {

??????? view.CurrentTime = DateTime.Now;

??? }

?

??? private ICurrentTimeView view;

}

完成上述代碼,我們就完成了Unit Test,mock view,Presenter和View.單元測試現在可以成功編譯并通過。下一個步驟是創建ASPX頁面充當真正的View。

注意到ArgumentNullException異常的檢查,這項技術被稱為基于契約設計(Design By Contract),在代碼中象這樣做必要的檢查可以大大的降低Bug的數量。關于基于契約設計(Design By Contract)的更多信息請參考http://archive.eiffel.com/doc/manuals/technology/contracthttp://www.codeproject.com/csharp/designbycontract.asp.

?

ShowMeTheTime.aspx - The View

這個頁面需要做以下內容:

l??????? ASPX頁面需要提供一個方法顯示當前的時間,用一個Label控件顯示時間

l??????? 后置代碼必須實現接口IcurrentTimeView

l??????? 后置代碼必須創建一個Presenter對象,并把自己傳遞給它的構造函數

l??????? 創建好Persenter對象后,需要調用InitView()

?

ASPX 頁面:

?

<asp:Label id="lblCurrentTime" runat="server" />

...

<

?

ASPX 后置代碼頁面:

?

public partial class ShowMeTheTime : Page, ICurrentTimeView

{

??? protected void Page_Load(object sender, EventArgs e) {

??????? CurrentTimePresenter presenter = new CurrentTimePresenter(this);

??????? presenter.InitView();

??? }

?

??? public DateTime CurrentTime {

???? ???set { lblCurrentTime.Text = value.ToString(); }

??? }

}

?

?

那就是MVP?

?

總而言之,是的,但是很有很多的內容。上面這個例子給你的不好印象是這么小的功能需要做那么多的工作。我們已經從創建ASPX頁面到一個Presenter類,一個View接口和一個單元測試類……,我們獲得的好處是對Presenter的單元測試,也就是很容易的對后置代碼頁面進行單元測試。這是一個最簡單的例子就像寫“Hello World”這樣。當構建企業級應用程序的時候就會體現出MVP模式的好處。下面的主題是企業級的ASP.NET應用中使用MVP模式。

?

?

在企業級ASP.NET應用中使用MVP

l??????? 使用用戶控件封裝Views:這個主題討論用戶控件作為MVP中的View

l??????? MVP的事件處理:這個主題討論連同頁面驗證傳遞事件到PresenterIsPostBack和將消息傳遞到View

l??????? MVP和PageMethods的頁面重定向:這個主題討論使用用戶控件作為View,如何使用PageMethods處理頁面重定向。

l??????? MVP的Presentation安全控制:這個主題討論如何根據基本的安全限制顯示/掩藏View中的區段

l??????? 使用MVP的應用的架構(高級):這是個重點,這個主題展示一個使用Nhibernate作為數據訪問層的MVP應用。

?

使用用戶控件封裝Views

?在上面的例子中,ASPX頁面充當View,把ASPX頁面做View只有一個簡單的目的顯示當前的時間。但是在一個比較有代表性的應用中,一個頁面通常包含一個或者多個功能性的區段,他們可能是WebPart,用戶控件等等。在企業級應用中,保持功能性的分離以及很容易的從一個地方移動到另一個地方是非常重要的。使用MVP,用戶控件用于封裝View,ASPX作為 “View Initializers”和頁面的重定向。擴展上面的例子,只要修改ASPX頁面的實現。這也是MVP的另一個好處,許多變化可以限制在View層而不要修改Presenter和Model。

?

ShowMeTheTime.aspx Redux - The View Initializer

用這種新的方式,ShowMeTheTime.aspx負責下列各項:

1.????? ASPX上面需要聲明實現ICurrentTimeView接口的用戶控件

2.????? 后置代碼必須創建一個Presenter對象,并把用戶控件傳遞給它的構造函數

3.????? 創建好Persenter對象后,需要調用InitView()

?

ASPX 頁面:...

<%@ Register TagPrefix="mvpProject" TagName="CurrentTimeView" Src="./Views/CurrentTimeView.ascx" %>

?

<mvpProject:CurrentTimeView id="currentTimeView" runat="server" />

...

?

The ASPX 后置代碼頁面:

?

public partial class ShowMeTheTime : Page // No longer implements ICurrentTimeView

{

??? protected void Page_Load(object sender, EventArgs e) {

??????? InitCurrentTimeView();

??? }

?

??? private void InitCurrentTimeView() {

??????? CurrentTimePresenter presenter = new CurrentTimePresenter(currentTimeView);

??????? presenter.InitView();

??? }

}

?

?

CurrentTimeView.ascx 用戶控件作為View

用戶控件現在充當View,完全取決于我們所期望的View是什么樣的

?

The ASCX 頁面:...

<asp:Label id="lblCurrentTime" runat="server" />

...

ASCX 后置代碼頁面:

public partial class Views_CurrentTimeView : UserControl, ICurrentTimeView

{

??? public DateTime CurrentTime {

??????? set { lblCurrentTime.Text = value.ToString(); }

??? }

}

?

使用用戶控件作為View的利弊

使用用戶控件作為MVP的View的主要缺點是添加另一個元素的方式。現在MVP元素由以下元素組成:unit test, presenter, view interface, view implementation (the user control) the view initializer (the ASPX page).使用用戶控件作為View的好處如下:

l??????? View非常容易的從一個頁面移到另一個頁面,這是大型的應用程序中經常發生的事

l??????? View在不需要復制代碼就可以在不同的頁面之間重用

l??????? View可以在不同的aspx頁面中進行初始化。例如一個用于顯示項目列表的用戶控件。在站點的報表區域用戶可能看并且可以過濾數據。在站點的另一個區域用戶只能看部分數據和不能使用過濾器。在實現方面,同一個View可以傳給相同的Presenter,但是不同的Aspx頁面可以調用Presenter的不同方法初始化View

l??????? 添加其他View到ASPX頁面并不需要額外的代碼,只需要將用戶控件添加到頁面,然后在后置代碼中把它和他的Presenter連接在一起就可以了。在同一頁面中沒有使用用戶控件管理不同的功能性區段,很快就會出現維護困難的問題。

?

MVP的事件處理

?

上面的例子,本質上描述的是一個Presenter同它的View之間的單向的通信。Presenter同Model通信,并把它傳遞給View。大多數情況下,引發的事件需要Presenter進行處理。此外一些事件依賴于頁面上的驗證是否通過或者是IsPostBack。例如數據綁定,在IsPostBack的時候不能被引發。

?

聲明:Page.IsPostBack和Page.IsValid是Web特有的。下面所討論的Presenter層只在Web環境中有效。但是只要做小小的修改,也能很好工作在Webform,Winform和Mobile應用中。無論如何,他們的理論基礎都是一樣的。

?

簡單的事件處理序列圖

?

繼續上面的例子,用戶可能要給當前時間上增加幾天,然后在View中顯示更新的時間,假設用戶輸入的是有效的數字,View中顯示的時間應等于當前時間加上增加的天數。當不是IsPostBack的時候,View顯示的事當前時間,當IsPostBack的時候,Presenter應當對事件作出回應。下面的序列圖表示了用戶的初始請求(上面部分)和用戶點擊按鈕”Add days”之后發生了什么.

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />

?

A)創建用戶控件

這一步只是表示ASPX頁面中聲明的用戶控件。在頁面初始化的時候,用戶控件被創建。在圖中表示的是實現接口IcurrentTimeView的用戶控件。在ASPX頁面的后置代碼的Page_Load事件,Presenter創建了一個實例,用戶控件作為參數通過構造函數傳遞給Presenter,到此為止,所有的描述的內容都和“使用用戶控件封裝Views”的一樣。

B) Presenter 添加到View

為了使事件能夠從View(用戶控件)傳遞到Presenter。View必須包含一個CurrentTimePresenter對象的引用,為了實現這個目的,View Initializer, ShowMeTheTime.aspx將Presnter傳遞給View。這不會造成Presenter和View之間的依賴,Presenter依賴于View的接口,View依賴于Presenter對事件的處理,讓我們代碼中看他們是如何工作的。

?

ICurrentTimeView.cs - The View Interface

public interface ICurrentTimeView {

??? DateTime CurrentTime { set; }

??? string Message { set; }

??? void AttachPresenter(CurrentTimePresenter presenter);

}

?

<A name=EventHandlingPresenter>CurrentTimePresenter.cs - The

Presenter</A></H3><PRE>public class CurrentTimePresenter {

??? public CurrentTimePresenter(ICurrentTimeView view) {

??????? if (view == null) throw new ArgumentNullException("view may not be null");

?

??????? this.view = view;

??? }

?

??? public void InitView(bool isPostBack) {

??????? if (! isPostBack) {

??????????? view.CurrentTime = DateTime.Now;

??????? }

??? }

?

??? public void AddDays(string daysUnparsed, bool isPageValid) {

??????? if (isPageValid) {

??????????? view.CurrentTime = DateTime.Now.AddDays(double.Parse(daysUnparsed));

??????? }

??????? else {

??????????? view.Message = "Bad inputs...no updated date for you!";

??????? }

??? }

?

??? private ICurrentTimeView view;

}

?

CurrentTimeView.ascx - The View

?

The ASCX Page:..

<asp:Label id="lblMessage" runat="server" /><br />

<asp:Label id="lblCurrentTime" runat="server" /><br />

<br />

<asp:TextBox id="txtNumberOfDays" runat="server" />

<asp:RequiredFieldValidator ControlToValidate="txtNumberOfDays" runat="server"

??? ErrorMessage="Number of days is required" ValidationGroup="AddDays" />

<asp:CompareValidator ControlToValidate="txtNumberOfDays" runat="server"

??? Operator="DataTypeCheck" Type="Double" ValidationGroup="AddDays"

??? ErrorMessage="Number of days must be numeric" /><br />

<br />

<asp:Button id="btnAddDays" Text="Add Days" runat="server"

??? OnClick="btnAddDays_OnClick" ValidationGroup="AddDays" />

...

?

The ASCX Code-Behind Page:

?

public partial class Views_CurrentTimeView : UserControl, ICurrentTimeView {

??? public void AttachPresenter(CurrentTimePresenter presenter) {

??????? if (presenter == null) throw new ArgumentNullException("presenter may not be null");

?

??????? this.presenter = presenter;

? ??}

?

??? public string Message {

??????? set { lblMessage.Text = value; }

??? }

?

??? public DateTime CurrentTime {

??????? set { lblCurrentTime.Text = value.ToString(); }

??? }

?

??? protected void btnAddDays_OnClick(object sender, EventArgs e) {

??????? if (presenter == null) throw new FieldAccessException("presenter has not yet been initialized");

?

??????? presenter.AddDays(txtNumberOfDays.Text, Page.IsValid);

??? }

?

??? private CurrentTimePresenter presenter;

}

?

ShowMeTheTime.aspx - The View Initializer

The ASPX Page:...

<%@ Register TagPrefix="mvpProject" TagName="CurrentTimeView" Src="./Views/CurrentTimeView.ascx" %>

?

<mvpProject:CurrentTimeView id="currentTimeView" runat="server" />

...

The ASPX Code-Behind Page:

public partial class ShowMeTheTime : Page? // No longer implements ICurrentTimeView

{

??? protected void Page_Load(object sender, EventArgs e) {

??????? InitCurrentTimeView();

??? }

?

??? private void InitCurrentTimeView() {

??????? CurrentTimePresenter presenter = new CurrentTimePresenter(currentTimeView);

??????? currentTimeView.AttachPresenter(presenter);

??????? presenter.InitView(Page.IsPostBack);

??? }

}

?

C) Presenter InitView

如需求所定義的,如果不是IsPostBack,Presenter只是顯示當前的時間。Presenter要知道在IsPostBack的時候該做些什么,這不應該由Aspx的后置代碼來決定。在上面的代碼中你看到了Aspx的后置代碼中沒有IsPostBack的處理。它只是簡單將值傳給Presenter,由Presenter來決定執行什么樣的動作。

這可能導致一個問題:“如果是另一個用戶控件引發的Post-back將會發生什么呢”。在這個例子中,當前的時間會保存在Label控件的ViewState中而再次顯示在Label控件上,這些都依賴客戶的需要。總體上,這是一個Presenter的好問題 另一個用戶控件引發的Post-back對這個用戶控件的影響。即使你沒有使用MVP,也是一個好問題。


?

When not IsPostBack, the Presenter then sets the <CODE>CurrentTime</CODE> of the view

via its interface. (Sequence diagram purists may raise the point that the

diagram implies two messages are being sent - one from CurrentTimePresenter to

ICurrentTimeView and then one from ICurrentTimeView to CurrentTimeView.ascx -

when in fact only one is being sent from CurrentTimePresenter to

CurrentTimeView.ascx, polymorphically. The interface "middleman" is included to

emphasize that the Presenter does not depend on the concrete View directly.)

D) Presenter InitView after IsPostBack

?

In the preceding steps, the user made the HTTP request, the Presenter set the

current time on the View, and the HTTP response was delivered to the user. Now,

the user clicks the "Add Days" button which causes a post-back. Everything

occurs as before until <CODE>InitView</CODE> is called on the Presenter. At this

point, the Presenter tests for IsPostBack and does <I>not</I> set the

<CODE>CurrentTime</CODE> on the View.

?

E) Button Click Handled by User Control

?

After the Page_Load of the ASPX page has occurred, the OnClick event is then

raised to the user control. The View should not handle the event itself; it

should immediately pass the event on to the Presenter for action. By looking at

the <A href="#EventPasser">code-behind of the user control</A>, you can see that

it makes sure it has been given a valid presenter (more "Design by Contract")

and then hands the command off to the Presenter. then verifies that the page was

valid and sets the time or error message, accordingly.

The above has been an exhaustive analysis of a complete MVP cycle with event

handling. Once you get the hang of MVP, it takes very little time to get all the

pieces in place. Remember to always begin with a unit test and let the unit

tests drive the development. The unit tests not only help ensure that the MVP

pieces are working correctly, they also serve as the point for defining the

communications protocol among the pieces. (A Visual Studio code snippet for an

MVP unit test can be found in We'll now

take a look at look at handling page redirection.

?

III. Page Redirects with MVP & PageMethods

In developing enterprise application, application flow is always a concern.

Who's going to take care of page redirects? Should action redirects be stored in

a configurable XML file? Should a third party tool such as <A

href="http://mavnet.sourceforge.net/" target=_blank>Maverick.NET</A> or <A

href="http://www.springframework.net/" target=_blank>Spring.NET</A> handle page

flow? Personally, I like to keep the page redirects as close to the action as

possible. In other words, I feel that storing action/redirects in an external

XML file leads to further indirection that can be tedious to understand and

maintain. (As if we don't have enough to worry about already!) On the other

hand, hard-coded redirects in the ASPX code-behind are fragile, tedious to parse

and not strongly typed. To solve this problem, PageMethods, free for download at

<A href="http://metasapiens.com/PageMethods"

target=_blank>http://metasapiens.com/PageMethods</A>, allows you to have

strongly typed redirects. So instead of writing <CODE>Response.Redirect("../Project/ShowProjectSummary?projectId=" +

projectId.ToString() + "&userId=" +

userId.ToString())</CODE>, PageMethods provides a strongly typed redirect that

would look more like

<CODE>Response.Redirect(MyPageMethods.ShowProjectSummary.ShowSummaryFor(projectId,

userId))</CODE>. The redirect is strongly typed and, therefore, checked at

compile time. </P>

<P>An MVP related question concerning page redirects remains: who should be

responsible for making a redirect and how should the redirect be initiated? (I

believe there are a number of valid answers to this question but will propose a

solution that I've found to be rather successful.) Add one event to the

Presenter for each outcome that is possible. For example, assume a website is

made up of two pages. The first page lists a number of projects; the second

page, reached by clicking "Edit" next to one of the project names, allows the

user to update the project's name. After updating the project name, the user

should be redirected to the project listing page again. To implement this, the

Presenter should raise an event showing that the project name was successfully

changed and then the View Initializer, the ASPX page, should execute the

appropriate redirect. (Note that the following is illustrative and not

associated with the "current time" example discussed thus far.)

<H4>Presenter:</H4><PRE>...

public event EventHandler ProjectUpdated;

?

public void UpdateProjectNameWith(string newName) {

??? ...

?

??? if (everythingWentSuccessfully) {

??????? ProjectUpdated(this, null);

??? }

??? else {

??????? view.Message = "That name already exists.? Please provide a new one!";

??? }

}

...

ASPX Code-Behind...

protected void Page_Load(object sender, EventArgs e) {

??? EditProjectPresenter presenter = new EditProjectPresenter(editProjectView);

??? presenter.ProjectUpdated += new EventHandler(HandleProjectUpdated);

??? presenter.InitView();

}

?

private void HandleProjectUpdated(object sender, EventArgs e) {

??? Response.Redirect(MyPageMethods.ShowProjectSummary.Show(projectId, userId));

}

...

</PRE>Taking this approach keeps page redirection out of the Presenter and out

of the View. As a rule of thumb, the Presenter should never require a reference

to <CODE>System.Web</CODE>. Furthermore, disassociating redirects from the View

- the user control - allows the View to be used again by other View Initializers

- other ASPX pages - while leaving application flow up to each individual View

Initializer. This is the greatest benefit of using an event based model of

redirection with User Control-as-View MVP.

?

IV. Presentation Security with MVP

?

Often times, a column, button, table or whatever should be shown/hidden based

on the permissions of the user viewing the website. Likewise, an item may be

hidden when a View is included in one View Initializer vs. being included in

different View Initializer. The security should be determined by the Presenter

but the View should handle how that decision should be implemented. Picking up

again with the "current time" example, assume that the client only wants the

"Add Days" section to be available for users on even days; e.g. 2, 4, 6, etc.

(The client likes to keep the users guessing!) The View could encapsulate this

area within a panel, as follows:

...

<asp:Panel id="pnlAddDays" runat="server" visible="false">

??? <asp:TextBox id="txtNumberOfDays" runat="server" />

??? <asp:RequiredFieldValidator ControlToValidate="txtNumberOfDays" runat="server"

??????? ErrorMessage="Number of days is required" ValidationGroup="AddDays" />

??? <asp:CompareValidator ControlToValidate="txtNumberOfDays" runat="server"

??????? Operator="DataTypeCheck" Type="Double" ValidationGroup="AddDays"

??????? ErrorMessage="Number of days must be numeric" /><br />

??? <br />

??? <asp:Button id="btnAddDays" Text="Add Days" runat="server"

??????? OnClick="btnAddDays_OnClick" ValidationGroup="AddDays" />

</asp:Panel>

...

?

Note that the panel's visibility is pessimistically set to <CODE>false</CODE>. Although it would not make much

difference in this case, it is better to be pessimistic about showing secure

elements than the other way around. The code-behind of the View would then

expose a setter to show/hide the panel: ...

public bool EnableAddDaysCapabilities {

??? set { pnlAddDays.Visible = value; }

}

...

?

Note that the View does not expose the panel directly. This is intentionally

done for two reasons: 1) exposing the panel directly would require that the

Presenter have a reference to <CODE>System.Web</CODE> - something we want to

avoid, and 2) exposing the panel ties the Presenter to an "implementation

detail" of the View. The more a Presenter is tied to how a View is implemented,

the less likely it will be reusable with other Views. As with other OOP

scenarios, the pros and cons of exposing implementation details of the View need

to be weighed against looser coupling to the Presenter.

Finally, during InitView, the Presenter checks if the user should be allowed

to use the add-days functionality and sets the permission on the View,

accordingly:

?

public void InitView() {

??? view.EnableAddDaysCapabilities = (DateTime.Now.Day % 2 == 0);

}

...

?

This simple example can be extended to a varied number of scenarios

including security checks. Note that this is not a replacement for built-in .NET

security, but it serves to augment it for finer control.

?

V. Application Architecture with MVP

Finally! How does all of this fit together in a data-driven, enterprise

application? "Enterprise application," in this instance, is an application that

has logically separated tiers including presentation, domain and data-access

layers. The following graph shows an overview of a fully architected solution

with discussion following.



each raised box represents a distinct specialization of the application. Each

gray box then represents a separate, physical assembly; e.g. MyProject.Web.dll,

MyProject.Presenters.dll, MyProject.Core.dll, etc. The arrows represent

dependencies. For example, the <I>.Web</I> assembly depends on the

<I>.Presenters</I> and <I>.Core</I> assemblies. The assemblies avoid

bi-directional dependency using the techniques Dependency Inversion and

Dependency Injection. My preferred means of Dependency Injection ("DI" in the

above graph) to the View Initializers is via the <A

href="http://www.castleproject.org/index.php/Windsor_Container"

target=_blank>Castle Windsor</A> project. The data layer then uses the ORM

framework, <A href="http://www.hibernate.org/343.html"

target=_blank>NHibernate</A>, for communicating with the database. </P>

<P>For a primer on Dependency Injection, read the CodeProject article entitled

"Dependency Injection for Loose Coupling" at <A

href="http://www.codeproject.com/csharp/DependencyInjection.asp"

target=_blank>http://www.codeproject.com/csharp/DependencyInjection.asp</A>.

Additionally, for a complete overview of this architecture, sans the

<I>.Presenters</I> layer and Castle Windsor integration, read the CodeProject

article entitled "NHibernate Best Practices with ASP.NET" at <A

href="http://www.codeproject.com/aspnet/NHibernateBestPractices.asp">http://www.codeproject.com/aspnet/NHibernateBestPractices.asp</A>.

This article also describes how to setup and run the sample application. (Yes,

these are both <I>shameless</I> plugs for other articles I have written - but

both required reading to fully appreciate the sample solution.) Please feel free

to raise any questions concerning the architecture.

?

In Summary

At first glance, implementing MVP looks like a lot of extra work. In fact, it

will slow development a bit during the <I>initial</I> stages of

development. But after using it in all stages of enterprise application

development, the long-term benefits of using the approach far outweigh the

initial feelings of discomfort with the pattern. MVP will greatly extend your

ability to unit test and keep code more maintainable throughout the lifetime of

the project - especially during the maintenance phase. When it comes right down

to it, I'm not suggesting that you use MVP on all your enterprise ASP.NET

projects, just the projects that you want to work! ;)? In all seriousness,

MVP is not appropriate in all situations.? An application's architecture

should fit the task at hand and complexity should not be added unless

warrented.? Obviously, MVP and User-Control-as-View MVP are

just two architectural options among many.? But, if used

appropriately, MVP allows you to be confident in your presentation logic by

making most of the code that would have been in a code-behind, testable and

maintainable.

?

Appendix A: Additional References

?

Martin Fowler's soon-to-be-published overview of MVP:

?

The humble dialog box:

.

?

<LI>MVC vs. MVP: <A

href="http://www.darronschall.com/weblog/archives/000113.cfm"

target=_blank>http://www.darronschall.com/weblog/archives/000113.cfm</A>.

<LI>A thank you goes out to Jeffrey Palermo for inspiring the above

architectural diagram at <A

href="http://codebetter.com/blogs/jeffrey.palermo/archive/<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />2005/04/02/128598.aspx"

target=_blank>http://codebetter.com/blogs/jeffrey.palermo/archive/2005/04/02/128598.aspx</A>. </LI></UL>

<P></P>

<H2><A name=AppendixB>Appendix B: MVP Unit Test Code-Snippet for Visual Studio

2005

?

With a default VS 2005 install location, copy the following contents to "MVP

Test Init.snippet" under "C:\Program Files\Microsoft Visual Studio

8\VC#\Snippets\1033\Visual C#." <PRE><?xml version="1.0" encoding="utf-8" ?>

<CodeSnippets? xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">

??? <CodeSnippet Format="1.0.0">

??????? <Header>

??????????? <Title>MVP Test Init</Title>

??????????? <Shortcut>mvpTestInit</Shortcut>

??????????? <Description>Code snippet for creating an initial unit test for a new MVP setup.</Description>

??????????? <Author>Billy McCafferty</Author>

??????????? <SnippetTypes>

??????????????? <SnippetType>Expansion</SnippetType>

??????????? </SnippetTypes>

??????? </Header>

??????? <Snippet>

??????????? <Declarations>

??????????????? <Literal>

??????????????????? <ID>viewInterface</ID>

??????????????? ????<ToolTip>Name of the view interface</ToolTip>

??????????????????? <Default>IView</Default>

??????????????? </Literal>

??????????????? <Literal>

??????????????????? <ID>presenter</ID>

??????????????????? <ToolTip>Name of the presenter class</ToolTip>

? ??????????????????<Default>Presenter</Default>

??????????????? </Literal>

??????????????? <Literal>

??????????????????? <ID>mockView</ID>

??????????????????? <ToolTip>Name of the mock view used in the unit test</ToolTip>

??????????????????? <Default>MockView</Default>

??????????????? </Literal>

??????????? </Declarations>

??????????? <Code Language="csharp">

??????????? <![CDATA[??????? [Test]

??????? public void TestInitView() {

??????????? $viewInterface$ view = new $mockView$();

??????????? $presenter$ presenter = new $presenter$(view);

??????????? view.AttachPresenter(presenter);

??????????? presenter.InitView();

??????? }

?

??????? private class $mockView$ : $viewInterface$

??????? {

??????????? public void AttachPresenter($presenter$ presenter) {

????? ??????}

??????? }

??????????????? $end$]]>

??????????? </Code>

??????? </Snippet>

??? </CodeSnippet>

</CodeSnippets>

?

總結

以上是生活随笔為你收集整理的使用ASP.NET实现Model View Presenter(MVP)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。