在使用模型綁定和網頁表單的專案中加入商業邏輯層

由 Tom FitzMacken 撰寫

本教學系列示範在 ASP.NET Web Forms 專案中使用模型綁定的基本面向。 模型綁定使資料互動比處理資料來源物件(如 ObjectDataSource 或 SqlDataSource)更為直接。 這個系列從入門教材開始,後續教學會進入更進階的概念。

本教學示範如何在商業邏輯層中使用模型綁定。 你會設定 OnCallingDataMethods 成員,指定使用非當前頁面的物件來呼叫資料方法。

本教學建立在 系列早期 專案的基礎上。

你可以 下載 使用 C# 或 VB 的完整專案。 可下載的程式碼可搭配 Visual Studio 2012 或 Visual Studio 2013 使用。 它使用 Visual Studio 2012 模板,與本教學中展示的 Visual Studio 2013 模板略有不同。

你將會建構什麼

模型綁定讓你能將資料互動程式碼放入網頁的程式碼背後檔案,或是獨立的商業邏輯類別中。 之前的教程已經展示了如何利用後置程式碼檔案來進行資料互動。 這種方法適用於小型站點,但可能導致程式碼重複及維護大型站點時的難度增加。 而且,以程式化方式測試後置程式碼中的程式碼可能會變得非常困難,因為沒有抽象層。

為了集中資料互動程式碼,你可以建立一個包含所有與資料互動邏輯的商業邏輯層。 接著你從網頁呼叫商業邏輯層。 這個教學示範如何將你在前幾次教學中寫的所有程式碼移到商業邏輯層,然後從頁面中使用這些程式碼。

在本教學課程中,您將會:

  1. 將程式碼從程式碼背後的檔案移到商業邏輯層
  2. 將資料綁定控制變更為呼叫業務邏輯層的方法

建立商業邏輯層

接著,你將建立從網頁中被呼叫的類別。 這堂課的方法看起來和你之前教學中使用的方法相似,並包含了 value provider 屬性。

首先,新增一個叫做 BLL 的資料夾。

新增資料夾

在 BLL 資料夾中,建立一個名為 SchoolBL.cs 的新類別。 它會包含所有原本存在於程式碼背後檔案中的資料操作。 這些方法幾乎與程式碼背後檔案中的方法相同,但會包含一些變更。

最重要的是,你不再是在 Page 類別的實例中執行程式碼。 Page 類別包含 TryUpdateModel 方法與 ModelState 屬性。 當這段程式碼移至商業邏輯層時,你就不再有 Page 類別的實例來呼叫這些成員。 要解決這個問題,你必須對任何存取 TryUpdateModel 或 ModelState 的方法加入 ModelMethodContext 參數。 你可以使用這個 ModelMethodContext 參數來呼叫 TryUpdateModel 或取得 ModelState。 你不需要在網頁中更改任何東西來因應這個新參數。

請將SchoolBL.cs中的程式碼替換為以下程式碼。

using System;
using System.Linq;
using ContosoUniversityModelBinding.Models;
using System.Web.ModelBinding;
using System.Web.UI.WebControls;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;

namespace ContosoUniversityModelBinding.BLL
{
    public class SchoolBL : IDisposable
    {
        SchoolContext db = new SchoolContext();

        public IQueryable<Student> GetStudents([Control] AcademicYear? displayYear)
        {
            var query = db.Students.Include(s => s.Enrollments.Select(e => e.Course));

            if (displayYear != null)
            {
                query = query.Where(s => s.Year == displayYear);
            }

            return query;
        }

        public void InsertStudent(ModelMethodContext context)
        {
            var item = new Student();

            context.TryUpdateModel(item);
            if (context.ModelState.IsValid)
            {
                db.Students.Add(item);
                db.SaveChanges();
            }
        }

        public void DeleteStudent(int studentID, ModelMethodContext context)
        {
            var item = new Student { StudentID = studentID };
            db.Entry(item).State = EntityState.Deleted;
            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException)
            {
                context.ModelState.AddModelError("",
                    String.Format("Item with id {0} no longer exists in the database.", studentID));
            }
        }

        public void UpdateStudent(int studentID, ModelMethodContext context)
        {
            Student item = null;
            item = db.Students.Find(studentID);
            if (item == null)
            {
                context.ModelState.AddModelError("", String.Format("Item with id {0} was not found", studentID));
                return;
            }

            context.TryUpdateModel(item);
            if (context.ModelState.IsValid)
            {
                db.SaveChanges();
            }
        }

        public IQueryable<Enrollment> GetCourses([QueryString] int? studentID)
        {
            var query = db.Enrollments.Include(e => e.Course)
                .Where(e => e.StudentID == studentID);
            return query;
        }

        private bool disposedValue = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposedValue)
            {
                if (disposing)
                {
                    db.Dispose();
                }
            }
            this.disposedValue = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}

修訂現有頁面以從商業邏輯層擷取資料

最後,你會將頁面Students.aspx、AddStudent.aspx和Courses.aspx,從程式碼背後檔案的查詢轉換成商業邏輯層。

在 Students、AddStudent 和 Courses 的程式碼隱含檔案中,請刪除或註解掉以下的查詢方法:

  • studentsGrid_GetData
  • studentsGrid_UpdateItem
  • studentsGrid_DeleteItem
  • addStudentForm_InsertItem
  • coursesGrid_GetData

你現在在代碼背後檔案中應該不會有與資料操作相關的程式碼。

OnCallingDataMethods 事件處理程式允許你指定一個物件來執行資料方法。 在 Students.aspx 中,為該事件處理器新增一個值,並將資料方法的名稱改為商業邏輯類別中的方法名稱。

<asp:GridView runat="server" ID="studentsGrid"
    ItemType="ContosoUniversityModelBinding.Models.Student" DataKeyNames="StudentID"
    SelectMethod="GetStudents"
    UpdateMethod="UpdateStudent" DeleteMethod="DeleteStudent"
    AllowSorting="true" AllowPaging="true" PageSize="4"
    AutoGenerateEditButton="true" AutoGenerateDeleteButton="true"
    AutoGenerateColumns="false" 
    OnCallingDataMethods="studentsGrid_CallingDataMethods">

在 Students.aspx 的程式碼背後檔案中,定義 CallingDataMethods 事件的事件處理程序。 在這個事件處理程序中,你可以指定用於資料操作的商業邏輯類別。

protected void studentsGrid_CallingDataMethods(object sender, CallingDataMethodsEventArgs e)
{
    e.DataMethodsObject = new ContosoUniversityModelBinding.BLL.SchoolBL();
}

在 AddStudent.aspx 中,進行類似的更改。

<asp:FormView runat="server" ID="addStudentForm"
    ItemType="ContosoUniversityModelBinding.Models.Student"
    InsertMethod="InsertStudent" DefaultMode="Insert"
    OnCallingDataMethods="addStudentForm_CallingDataMethods"
    RenderOuterTable="false" OnItemInserted="addStudentForm_ItemInserted">
protected void addStudentForm_CallingDataMethods(object sender, CallingDataMethodsEventArgs e)
{
    e.DataMethodsObject = new ContosoUniversityModelBinding.BLL.SchoolBL();
}

Courses.aspx: 進行類似的更改。

<asp:GridView runat="server" ID="coursesGrid"
    ItemType="ContosoUniversityModelBinding.Models.Enrollment"
    SelectMethod="GetCourses" AutoGenerateColumns="false"
    OnCallingDataMethods="coursesGrid_CallingDataMethods">
protected void coursesGrid_CallingDataMethods(object sender, CallingDataMethodsEventArgs e)
{
    e.DataMethodsObject = new ContosoUniversityModelBinding.BLL.SchoolBL();
}

執行應用程式後,發現所有頁面都和之前一樣正常。 驗證邏輯也正常運作。

結論

在這個教學中,你重新結構了應用程式,使用資料存取層和商業邏輯層。 你指定資料控制使用一個不是目前頁面的物件來進行資料操作。