2013年5月13日 星期一

ASP.NET WebForm 使用分層的 Repository 類別庫專案

這一篇的內容是延續前三篇文章的架構內容:

ASP.NET MVC 的 Model 使用 ADO.NET

ASP.NET MVC 的 Model 使用 Enterprise Library 6 Data Access Application Block

ASP.NET MVC - 使用 Simple Injector 讓 Model 三選一

只不過 Web 專案要換成 ASP.NET WebForm,因為當初在公司內部教育訓練時,公司其他同事都沒有 ASP.NET MVC 的開發經驗,雖然其中一位同事還買了保哥的書,但從來沒看見他翻開來看過,所以在講 ASP.NET Model 的時候就是先說明 ADO.NET 來做為 ASP.NET MVC Model 的內容,再進而介紹 Enterprise Library Data Access Application Block 與 ADO.NET Entity Framework,但公司主要的開發還是以使用 ASP.NET WebForm 為主,雖然短時間內公司不會使用 ASP.NET MVC 來開發新案,但至少可以有一些觀念與作法可以帶到開發 ASP.NET WebForm 時可以使用,所以就沿用既有已經開發好給 MVC 網站專案使用的 Repository,讓同事們了解先定義好 Domain 物件類別、定義介面、實作介面、套用在 Web 專案上,這一系列的作法不會因為開發 MVC 或是 Web 而有任何的差異,差異只有在 Web 專案裡的作法而已。

再來就是有位朋友在「ASP.NET MVC 的 Model 使用 ADO.NET」這篇文章提出問題:

image

雖然這個系列的文章並非三層架構,但還是做到將資料存取這個部分給抽離出 Web 專案,而且這樣的作法不管是在 ASP.NET WebForm 或 ASP.NET MVC 都可以使用。

 


Data Source Control?DataSet?DataTable?

其實老實說…… 我好久沒有用過 Data Source Control 了,SqlDataSource 很久不見了,LinqDataSource …… 幾年前的文章有寫怎麼使用,但也只有用那麼幾次,ObjectSource 我承認我根本不會用,DataSet, DataTable 我做報表的時候才會用到。

是的,我也是有開發 ASP.NET WebForm,但我就是不會去用 Data Source Control,除非必要不然也不會用 DataSet,我想看到這裡應該有人就會覺得這樣怎麼開發 ASP.NET WebForm 呢?GridView, ListView, Repeater 等等伺服器控制項又要怎麼套用資料呢?

還記得幾年前曾經到新店某行動裝置製造公司參觀過,那裡的主管一聽我開發 ASP.NET WebForm 不用 DataSource Control 時就直接回我說「這樣開發程式怎麼會快呢?」是呀,是不會快多少,我當時回應那位主管說不用 DataSource Control 在事後的維護絕對會更好處理!不過那位主管一臉不敢置信的表情讓我印象深刻。

不是說 DataSource Control 不好,只是我不喜歡存取資料還要被另一個控制項給箝制住,例如使用 SqlDataSource 有個讓我很感冒的一點就是,為何要讓 SQL Statement 放在 ASPX 上呢?程式裡面都不想看到了更何況是在 ASPX 裡。

有時撇開這些看似幫我們處理資料與控制項之間溝通功能的 DataSource Control,可以讓專案的開發更有彈性。

 

加入 ASP.NET WebForm 專案

延續之前文章的架構,加入 ASP.NET WebForm 專案,

image

接著加入 Sample.Domain, Sample.Repository.Interface 以及 Sample.Repository.ADONET 的專案參考,這邊我們先不用急著把另外兩個 Repository Implement 的專案參考給加進來,先看看怎麼在 WebForm 專案怎麼使用 Sample.Repository.ADO.NET,

image

再來就是從 Sample.Web.MVC 專案裡的 Web.Config 複製 ConnectionString,

image

建立 Employee 目錄,另外建立 List.aspx 與 Details.aspx

image

 

List.aspx 使用 Sample.Repository.ADONET

在 List.aspx 裡加入 GridView 並且設定好要顯示的資料,

image

image

接著就是完成 Code Behind 的程式內容,與在 ASP.NET MVC Controller 一樣,會宣告一個型別為 IEmployeeRepository 的私有變數,然後在建構式裡建立 Sample.Repository.ADONET 的 EmployeeRepository 實例,

image

然後就是把使用 EmployeeRepository 裡頭的方法所取得的資料給頁面上的 GridView 控制項,

image

當然也別忘了 GridView1 的 RowDataBound() 事件處理,

image

List.aspx 的 Code Behind 完整程式如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Sample.Repository.Interface;
using Sample.Repository.ADONET;
 
namespace Sample.Web.WebForm.Employee
{
    public partial class List : System.Web.UI.Page
    {
        private IEmployeeRepository _repository;
 
        public List()
        {
            this._repository = new EmployeeRepository();
        }
 
 
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!this.IsPostBack)
            {
                this.EnployeesDataBind();
            }
        }
 
        private void EnployeesDataBind()
        {
            var employees = this._repository.GetEmployees();
            this.GridView1.DataSource = employees;
            this.GridView1.DataKeyNames = new string[] { "EmployeeID" };
            this.GridView1.DataBind();
        }
 
        protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
        {
            if (e.Row.RowType == DataControlRowType.DataRow)
            {
                object primaryKey = GridView1.DataKeys[e.Row.RowIndex]["EmployeeID"];
                HyperLink HyperLink_Details = e.Row.FindControl("HyperLink_Details") as HyperLink;
 
                int employeeID;
                if (int.TryParse(primaryKey.ToString(), out employeeID)
                    && HyperLink_Details != null)
                {
                    HyperLink_Details.NavigateUrl = string.Concat("Details.aspx?id=", employeeID.ToString());
                }
            }
        }
 
    }
}

執行網站來檢視這一頁的執行結果,

image

 

Details.asp 使用 Sample.Repository.ADONET

設計好 Details.aspx 的頁面內容,

image

Code Behind 的程式內容:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Sample.Repository.Interface;
using Sample.Repository.ADONET;
 
namespace Sample.Web.WebForm.Employee
{
    public partial class Details : System.Web.UI.Page
    {
        private IEmployeeRepository _repository;
 
        public Details()
        {
            this._repository = new EmployeeRepository();
        }
 
        private int employeeID;
        public int EmployeeID
        {
            get { return employeeID; }
            set { employeeID = value; }
        }
 
        private Sample.Domain.Employee instance;
        public Sample.Domain.Employee Instance
        {
            get
            {
                if (this.instance == null)
                {
                    var employee = this._repository.GetOne(this.EmployeeID);
                    this.instance = employee;
                }
                return instance;
            }
        }
 
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                this.SetDefault();
            }
        }
 
        private void SetDefault()
        {
            int id;
            if (Request.QueryString["id"] == null)
            {
                Response.Redirect("List.aspx");
            }
            else
            {
                if (!int.TryParse(Request.QueryString["id"].Trim(), out id))
                {
                    Response.Redirect("List.aspx");
                }
                else
                {
                    this.EmployeeID = id;
                    if (this.Instance == null)
                    {
                        Response.Redirect("List.aspx");
                    }
                    else
                    {
                        this.Label_EmployeeID.Text = this.Instance.EmployeeID.ToString();
                        this.TextBox_LastName.Text = this.Instance.LastName;
                        this.TextBox_FirstName.Text = this.Instance.FirstName;
                        this.TextBox_Title.Text = this.Instance.Title;
                        this.TextBox_BirthDate.Text = this.Instance.BirthDate.ToString("yyyy/MM/dd");
                        this.TextBox_HireDate.Text = this.Instance.HireDate.ToString("yyyy/MM/dd");
                    }
                }
            }
        }
    }
}

Details.aspx 執行結果

image

 

由上面的 List.aspx 與 Details.aspx 程式內容來看,我們並沒有在程式裡有任何的資料存取操作內容,也沒有使用任何的 DataSource Control,而是透過之前就已經建立好並且是給 ASP.NET MVC 網站專案使用的 Sample.Repository.ADONET。

由此可以了解到,不管是 ASP.NET MVC 網站或是 ASP.NET WebForm 網站並不需要在專案本身裡去實作資料存取的方法,可以將這個部分給抽離出來,而抽離出來的 Repository 專案還可以給不同的表現層專案使用,共同的是使用同一個 Repository 專案,只是各個展現層專案在 UI 呈現的操作與方式各有不同罷了。

所以我們將資料存取以及 Domain 類別分別建立成專案的作法只限於使用在 ASP.NET MVC 專案上嗎?

看過這篇文章的簡單示範就應該可以了解到不管 WebForm 還是 MVC 網站都是沒有任何限制的。

如果還要加上商業邏輯層呢?

也是一樣的作法,只不過千萬要記得不要把控制項的操作也帶進商業邏輯層裡。

就講到這裡,下一篇再來看看如何在 ASP.NET WebForm 網站專案裡使用 Simple Injector 切換使用不同的 Repository Implement 專案。

 

相關系列文章

ASP.NET MVC

ASP.NET MVC 的 Model 使用 ADO.NET

ASP.NET MVC 的 Model 使用 Enterprise Library 6 Data Access Application Block

ASP.NET MVC - 使用 Simple Injector 讓 Model 三選一

ASP.NET WebForm

ASP.NET WebForm 使用分層的 Repository 類別庫專案

ASP.NET WebForm 使用 Simple Injector 選擇不同的 Repository

範例原始檔

ASP.NET MVC 與 ASP.NET WebForm 使用 Simple Injector 切換選擇不同 Repository 原始碼下載

 

以上

2 則留言:

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。