[ASP.NET] 實做 MVC + CRUD Pattern

[ASP.NET] : 實做 MVC + CRUD Pattern


前言 :

[ASP.NET] : WebForm MVC Pattern
[ASP.NET] : WebForm CRUD Pattern
上面兩篇為之前發表過的兩個ASP.NET的Pattern,內容主要是在描述MVC的分工以及CRUD的頁面。
這篇文章綜合上述的兩個Pattern來實做,以期能更清楚描述出CRUD的樣版以及MVC的精神。


物件導向的程式,因為把職責分散到各個物件去看起來會很零碎。
可能要參照上述兩篇文章內容,才能將程式碼在腦海裡組織起來。
(另外也想傳達物件導向程式撰寫時的思路,所以會顯的囉嗦點...)


系統架構 :

下圖的詳細內容可以參照之前的兩篇文章。


UserData And UserStore :

UserData : 新增修改刪除查詢(CRUD)的物件。
UserStore : 跟儲存裝置做連接的資料連接層。
而在UserStore可以使用UserData的Id來查詢UserData。

public class UserData
{
    public Guid Id;

    public string Name;

    public string Description;
}

public class UserStore
{
    // Operate
    public void Insert(UserData user) { }

    public void Update(UserData user) { }

    public void Delete(UserData user) { }

    // Find
    public IEnumerable FindAll() { return null; }

    public UserData FindOne(Guid id) { return null; }
}

*UserStore內容比較多就不條列了,主要是依照儲存裝置撰寫相應的ORM。


UserManageModel :

UserManageModel 主要負責MVC內Model的職責。
在這邊除了上述職責之外,也將 UserStore生成的工作擺在這一層。
並且包裝UserStore,將UserStore跟整個ASP.NET的頁面做隔離。

public class UserManageModel
{
    // Member       
    private UserStore CreateUserStore()
    {
        return new UserStore();
    }

    public void Insert(UserData user)
    {
        UserStore store = this.CreateUserStore();
        store.Insert(user);
    }

    public void Update(UserData user)
    {
        UserStore store = this.CreateUserStore();
        store.Update(user);
    }

    public void Delete(IEnumerable userCollection)
    {
        UserStore store = this.CreateUserStore();
        foreach (UserData user in userCollection)
        {
            store.Delete(user);
        }
    }

    public IEnumerable FindAllUser()
    {
        UserStore store = this.CreateUserStore();
        IEnumerable userCollection = store.FindAll();
        return userCollection;
    }

    public UserData FindOneUser(Guid id)
    {
        UserStore store = this.CreateUserStore();
        UserData user = store.FindOne(id);
        return user;
    }
}

IUserManageController :

IUserManageController主要負責MVC內Controller的職責。
但是這邊只是定義介面(原因前篇文章有描述),實際完成Controller的內容是建立在UserManageConsole。


在這個介面內每個Function,主要是由頁面功能來定義出來的。
Index() : 進入顯示全部使用者資料功能。
Index(Guid userId) : 進入顯示單一使用者資料功能。
Insert() : 進入新增使用者功能。
Insert(UserData user) : 新增使用者功能。
Update(Guid id) : 進入修改使用者功能。
Update(UserData user) : 修改使用者功能。
Delete(IEnumerable<Guid> idCollection) : 進入刪除使用者功能。
Delete(IEnumerable<UserData> userCollection) : 刪除使用者功能。

public interface IUserManageController
{
    // Index
    void Index();

    void Index(Guid id);

    // Operate
    void Insert();

    void Insert(UserData user);

    void Update(Guid id);

    void Update(UserData user);

    void Delete(IEnumerable idCollection);

    void Delete(IEnumerable userCollection);
}

UserManageConsole :

為程式的主要進入點,是一個WebForm。
UserManageConsole實際完成IUserManageController的職責。
在這邊除了上述職責之外,也將所有View、還有Model生成的工作擺在這一層。

<%@ Register src="UserListView.ascx" tagname="UserListView" tagprefix="uc1" %>
<%@ Register src="UserInsertView.ascx" tagname="UserInsertView" tagprefix="uc2" %>
<%@ Register src="UserNotifyView.ascx" tagname="UserNotifyView" tagprefix="uc3" %>
<!--...-->

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <uc1:UserListView ID="_listView" runat="server" Visible="false" />
        <uc2:UserInsertView ID="_insertView" runat="server" Visible="false" />
        <uc3:UserNotifyView ID="_notifyView" runat="server" Visible="false" />
        <!--...-->
    </form>
</body>
</html>


public partial class UserManageConsole : System.Web.UI.UserControl, IUserManageController
{
    // Properties
    private UserManageModel _model = null;

    // Construction
    protected void Page_Init(object sender, EventArgs e)
    {
        // Model
        _model = new UserManageModel();

        // View
        UserBaseView view;
        foreach (Control control in this.Controls)
        {
            if (control is UserBaseView)
            {
                view = (UserBaseView)control;
                view.Model = _model;
                view.Controller = this;
            }
        }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        if (this.IsPostBack == false)
        {
            this.Index();
        }
    }

    // Member
    private void ShowView(UserBaseView view)
    {
        foreach (Control control in this.Controls)
        {
            if (control is UserBaseView)
            {
                ((UserBaseView)control).Hidden();
                control.Visible = false;
            }
        }
        view.Visible = true;
    }

    #region IUserManageController 成員

    // Index
    public void Index()
    {
        this.ShowView(_listView);
        _listView.Show();
    }

    public void Index(Guid userId)
    {
        this.ShowView(_detailView);
        _detailView.Show(userId);
    }

    public void Insert()
    {
        this.ShowView(_insertView);
        _insertView.Show();
    }

    public void Insert(UserData user)
    {
        _model.Insert(user);

        this.ShowView(_notifyView);
        _notifyView.Show("新增資料完成");
    }

    public void Update(Guid userId)
    {
        this.ShowView(_updateView);
        _updateView.Show(userId);
    }

    public void Update(UserData user)
    {
        _model.Update(user);

        this.ShowView(_notifyView);
        _notifyView.Show("修改資料完成");
    }

    public void Delete(IEnumerable<Guid> userIdCollection)
    {
        this.ShowView(_deleteView);
        _deleteView.Show(userIdCollection);
    }

    public void Delete(IEnumerable<UserData> userCollection)
    {
        _model.Delete(userCollection);

        this.ShowView(_notifyView);
        _notifyView.Show("刪除資料完成");
    }

    #endregion
}


UserBaseView :

UserXxxxxxxView主要負責MVC內View的職責。由Controller來決定現在該顯示哪個View,並且接受使用者輸入及資料驗證。
UserBaseView 主要是將一些View內通用的功能提取出來,建立為View通用的父物件。
並且UserBaseView 本身為UserControl的延伸物件,所有的View也將都會是UserControl。

public class UserBaseView : System.Web.UI.UserControl
{
    // Member
    private UserManageModel _model = null;
    public UserManageModel Model
    {
        set
        {
            _model = value;
        }
        get
        {
            return _model;
        }
    }

    private IUserManageController _controller = null;
    public IUserManageController Controller
    {
        set
        {
            _controller = value;
        }
        get
        {
            return _controller;
        }
    }

    public void Hidden()
    {
        this.ClearChildState();
        this.ViewState.Clear();
    }
}


UserListView :

UserListView繼承UserBaseView。
UserListView在CRUD的職責主要是顯示所有使用者資料。
在使用者按下新增鍵的時候,呼叫Insert()進入新增使用者功能。
在使用者按下刪除鍵的時候,呼叫Delete(IEnumerable<Guid> idCollection)進入刪除使用者功能。

<!-- button -->
<asp:Button ID="Button1" runat="server" Text="新增" CommandName="Insert" OnCommand="Button_Command" />
<asp:Button ID="Button3" runat="server" Text="刪除" CommandName="Delete" OnCommand="Button_Command" />
<asp:Button ID="Button4" runat="server" Text="重新整理" CommandName="Refresh" OnCommand="Button_Command" />
<hr />

<!-- content -->
<h1>使用者列表</h1>
<hr />
<asp:ListView ID="_userListView" runat="server" DataKeyNames="Id">
    <LayoutTemplate>
       <table width="100%">
            <asp:PlaceHolder runat="server" ID="itemPlaceholder"></asp:PlaceHolder>
        </table>
    </LayoutTemplate>
    <ItemTemplate>       
        <tr>
            <td align="left" nowrap width="1%">
                <asp:CheckBox ID="_itemCheckBox" runat="server" />                               
            </td>
            <td align="left" nowrap width="1%">
                <asp:LinkButton ID="_nameLinkButton1" runat="server" CommandName="Display" CommandArgument='<%# Eval("Id") %>' OnCommand="Button_Command"><%# HttpUtility.HtmlEncode((string)Eval("Name")) %></asp:LinkButton>     
            </td>
            <td align="left">
                <asp:Label ID="_descriptionLabel" runat="server" Text='<%# HttpUtility.HtmlEncode((string)Eval("Description")) %>' ></asp:Label>                          
            </td>
        </tr>  
    </ItemTemplate>
</asp:ListView>


public partial class UserListView : UserBaseView
{
    // Member
    public void Show()
    {
    }

    private IEnumerable<Guid> GetSelectUserId()
    {
        List<Guid> guidList = new List<Guid>();

        foreach (ListViewDataItem dataItem in _userListView.Items)
        {
            CheckBox checkBox = dataItem.FindControl("_itemCheckBox") as CheckBox;
            if (checkBox != null)
            {
                if (checkBox.Checked == true)
                {
                    guidList.Add((Guid)(_userListView.DataKeys[dataItem.DisplayIndex].Value));
                }
            }
        }

        return guidList;
    }

    protected void Button_Command(object sender, CommandEventArgs e)
    {
        IEnumerable<Guid> guidCollection;
        switch (e.CommandName)
        {
            case "Insert":
                this.Controller.Insert();
                break;

            case "Delete":
                guidCollection = this.GetSelectUserId();
                if (guidCollection.Count() > 0)
                {
                    this.Controller.Delete(guidCollection);
                }
                break;

            case "Refresh":
                this.Controller.Index();
                break;

            case "Display":
                this.Controller.Index(new Guid(e.CommandArgument as string));
                break;
        }
    }
}


UserInsertView :

UserInsertView繼承UserBaseView。
UserInsertView在CRUD的職責主要是接受使用者輸入新使用者資料。
在使用者按下確認鍵的時候,呼叫Insert(UserData user)新增使用者功能。
在使用者按下取消鍵的時候,呼叫Index()進入顯示全部使用者資料功能。

<!-- button -->
<asp:Button ID="Button1" runat="server" Text="確認" CommandName="Apply" OnCommand="Button_Command" />
<asp:Button ID="Button2" runat="server" Text="取消" CommandName="Cancel" OnCommand="Button_Command" />
<hr />

<!-- content -->
<h1>使用者新增</h1> 
<hr />
Name : <asp:TextBox ID="_nameTextBox" runat="server" Text="" class="large_text"></asp:TextBox><br />
Description : <asp:TextBox ID="_descriptionTextBox" runat="server" Text="" class="large_text"></asp:TextBox>


public partial class UserInsertView : UserBaseView
{
    // Member
    public void Show()
    {
        _nameTextBox.Text = string.Empty;
        _descriptionTextBox.Text = string.Empty;
    }

    protected void Button_Command(object sender, CommandEventArgs e)
    {
        switch (e.CommandName)
        {
            case "Apply":
                UserData user = new UserData();
                user.Id = Guid.NewGuid();
                user.Name = _nameTextBox.Text;
                user.Description = _descriptionTextBox.Text;

                this.Controller.Insert(user);
                break;

            case "Cancel":
                this.Controller.Index();
                break;
        }
    }
}


UserNotifyView :

UserNotifyView繼承UserBaseView。
UserInsertView在CRUD的職責主要是顯示程式執行的通知訊息。
在使用者按下確認鍵的時候,呼叫Controller.Index()進入顯示全部使用者資料功能。

<!-- button -->
<asp:Button ID="Button1" runat="server" Text="確認" CommandName="Apply" OnCommand="Button_Command" />
<hr />

<!-- content -->
<h1>使用者通知</h1>
<asp:Label ID="_messageLabel" runat="server" Text="Label"></asp:Label>

public partial class UserNotifyView : UserBaseView
{
    // Member
    public void Show(string message)
    {
        _messageLabel.Text = message;
    }

    protected void Button_Command(object sender, CommandEventArgs e)
    {
        switch (e.CommandName)
        {
            case "Apply":
                this.Controller.Index();
                break;
        }
    }
}


OtherView :

剩下的View
UserDeleteView.ascx
UserDetailView.ascx
UserUpdateView.ascx
這幾個View跟上述幾個View大同小異就不贅述了。

期許自己
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。