使用 Entity Framework 4.0 和 ObjectDataSource 控制項,第 1 部分:消費者入門

作者 :Tom Dykstra

本教學課程系列是以 Contoso University Web 應用程式為基礎,此應用程式是由使用Entity Framework 4.0教學課程系列消費者入門所建立。 如果您未完成先前的教學課程,作為本教學課程的起點,您可以下載您已建立 的應用程式 。 您也可以下載完整教學課程系列所建立 的應用程式

Contoso University 範例 Web 應用程式示範如何使用 Entity Framework 4.0 和 Visual Studio 2010 建立 ASP.NET Web Forms應用程式。 範例應用程式是虛構 Contoso 大學的網站。 其中包括的功能有學生入學許可、課程建立、教師指派。

本教學課程顯示 C# 中的範例。 可下載的範例包含 C# 和 Visual Basic 中的程式碼。

Database First

您可以在 Entity Framework 中使用資料的方式有三種: Database FirstModel FirstCode First。 本教學課程適用于 Database First。 如需這些工作流程之間的差異,以及如何為您的案例選擇最佳工作流程的指引資訊,請參閱 Entity Framework 開發工作流程

Web Form

如同消費者入門系列,本教學課程系列使用 ASP.NET Web Forms模型,並假設您知道如何在 Visual Studio 中使用 ASP.NET Web Forms。 如果沒有,請參閱ASP.NET 4.5 Web Form消費者入門。 如果您想要使用 ASP.NET MVC 架構,請參閱使用 ASP.NET MVC 搭配 Entity Framework 消費者入門

軟體版本

本教學課程所示 也適用于
Windows 7 Windows 8
Visual Studio 2010 Visual Studio 2010 Express for Web。 本教學課程尚未使用較新版本的 Visual Studio 進行測試。 功能表選取專案、對話方塊和範本有許多差異。
.NET 4 .NET 4.5 與 .NET 4 回溯相容,但教學課程尚未使用 .NET 4.5 進行測試。
Entity Framework 4 本教學課程尚未使用較新版本的 Entity Framework 進行測試。 從 Entity Framework 5 開始,EF 預設 DbContext API 會使用 EF 4.1 引進的 。 EntityDataSource 控制項是設計來使用 ObjectContext API。 如需如何搭配 DbContext API 使用 EntityDataSource 控制項的詳細資訊,請參閱 此部落格文章

問題

如果您有與本教學課程不直接相關的問題,您可以將問題張貼至ASP.NET entity Framework 論壇Entity Framework 和 LINQ to Entities 論壇,或StackOverflow.com

控制項 EntityDataSource 可讓您非常快速地建立應用程式,但通常需要您在 .aspx 頁面中保留大量的商務邏輯和資料存取邏輯。 如果您預期應用程式的複雜度增加,而且需要持續維護,您可以預先投資更多開發時間,以建立 多層式或 多層 式應用程式結構 ,使其更容易維護。 若要實作此架構,請將展示層與 BLL (商務邏輯層分隔) 和資料存取層 (DAL) 。 實作此結構的其中一種方式是使用 ObjectDataSource 控制項,而不是 EntityDataSource 控制項。 當您使用 控制項時,您可以實作 ObjectDataSource 自己的資料存取程式碼,然後在 .aspx 頁面中使用與其他資料來源控制項具有相同許多功能的控制項來叫用它。 這可讓您結合多層式方法的優點,以及使用資料存取Web Form控制項的優點。

控制項 ObjectDataSource 也可讓您以其他方式更有彈性。 因為您撰寫自己的資料存取程式碼,所以比唯讀取、插入、更新或刪除特定實體類型更容易執行,這是 EntityDataSource 控制項設計來執行的工作。 例如,您可以在每次更新實體時執行記錄、每當刪除實體時封存資料,或在插入具有外鍵值的資料列時,視需要自動檢查和更新相關資料。

商務邏輯和存放庫類別

控制項 ObjectDataSource 的運作方式是叫用您建立的類別。 類別包含擷取和更新資料的方法,而且您會將這些方法 ObjectDataSource 的名稱提供給標記中的 控制項。 在轉譯或回傳處理期間,會 ObjectDataSource 呼叫您指定的方法。

除了基本 CRUD 作業之外,您建立以搭配 ObjectDataSource 控制項使用的類別可能需要在讀取或更新資料時 ObjectDataSource 執行商務邏輯。 例如,當您更新部門時,您可能需要驗證沒有其他部門擁有相同的系統管理員,因為一個人不能是多個部門的系統管理員。

在某些 ObjectDataSource 檔中,例如 ObjectDataSource 類別概觀,控制項會呼叫稱為 商務物件的 類別,其中包含商務邏輯和資料存取邏輯。 在本教學課程中,您將針對商務邏輯和資料存取邏輯建立個別的類別。 封裝資料存取邏輯的類別稱為存放 。 商務邏輯類別同時包含商務邏輯方法和資料存取方法,但資料存取方法會呼叫存放庫來執行資料存取工作。

您也會在 BLL 與 DAL 之間建立抽象層,以利 BLL 的自動化單元測試。 當您在商務邏輯類別中具現化存放庫時,會建立介面並使用 介面來實作這個抽象層。 這可讓您為商務邏輯類別提供實作存放庫介面之任何物件的參考。 針對正常作業,您會提供可搭配 Entity Framework 使用的存放庫物件。 為了進行測試,您會提供存放庫物件,其可搭配儲存的資料輕鬆操作,例如定義為集合的類別變數。

下圖顯示商務邏輯類別之間的差異,該類別包含不含存放庫的資料存取邏輯,以及使用存放庫的邏輯。

Image05

首先,您將建立控制項直接系結至存放庫的網頁 ObjectDataSource ,因為它只會執行基本資料存取工作。 在下一個教學課程中,您將建立具有驗證邏輯的商務邏輯類別,並將控制項系結至該類別,而不是系結 ObjectDataSource 至存放庫類別。 您也會建立驗證邏輯的單元測試。 在本系列的第三個教學課程中,您會將排序和篩選功能新增至應用程式。

您在本教學課程中建立的頁面會使用 Departments 您在消費者入門教學課程系列中建立之資料模型的實體集。

顯示 [部門] 頁面外觀的螢幕擷取畫面。

Image02

更新資料庫和資料模型

您將透過對資料庫進行兩項變更來開始本教學課程,這兩者都需要使用Entity Framework 和 Web Form教學課程在消費者入門中建立的資料模型對應變更。 在上述其中一個教學課程中,您已在設計工具中手動進行變更,以在資料庫變更之後,將資料模型與資料庫同步處理。 在本教學課程中,您將使用設計工具的 [從資料庫更新模型 ] 工具來自動更新資料模型。

將關聯性新增至資料庫

在 Visual Studio 中,使用Entity Framework 和 Web Form教學課程系列開啟您在消費者入門中建立的 Contoso University Web 應用程式,然後開啟 SchoolDiagram 資料庫關係圖。

如果您查看 Department 資料庫關係圖中的資料表,您會看到它有一個資料 Administrator 行。 此資料行是資料表的 Person 外鍵,但資料庫中未定義任何外鍵關聯性。 您必須建立關聯性並更新資料模型,讓 Entity Framework 可以自動處理此關聯性。

在資料庫關係圖中,以滑鼠右鍵按一下 Department 資料表,然後選取 [ 關聯性]。

Image80

在 [ 外鍵關聯性] 方塊中,按一下 [ 新增],然後按一下 [資料表和資料行規格] 的省略號。

Image81

在 [ 資料表和資料行 ] 對話方塊中,將主鍵資料表和欄位設定為 PersonPersonID ,並將外鍵資料表和欄位設定為 DepartmentAdministrator 。 (當您這樣做時,關聯性名稱會從 FK_Department_Department 變更為 FK_Department_Person .)

Image82

在 [資料表和資料行] 方塊中按一下[確定],按一下 [外鍵關聯性] 方塊中的[關閉],然後儲存變更。 如果系統詢問您是否要儲存 PersonDepartment 資料表,請按一下 [ ]。

注意

如果您已刪除 Person 對應至資料行中 Administrator 資料的資料列,您將無法儲存這項變更。 在此情況下,請使用 [伺服器 總管] 中的資料表編輯器, Administrator 確定每個 Department 資料列中的值都包含資料表中實際存在的記錄識別碼 Person

儲存變更之後,如果該人員是部門系統管理員,您將無法從 Person 資料表中刪除資料列。 在生產應用程式中,您會在資料庫條件約束防止刪除時提供特定的錯誤訊息,或指定串聯刪除。 如需如何指定串聯刪除的範例,請參閱Entity Framework 和 ASP.NET – 消費者入門第 2 部分

將檢視新增至資料庫

在您將建立的新 Departments.aspx 頁面中,您想要提供講師的下拉式清單,其名稱為 「最後一個,第一個」格式,讓使用者可以選取部門系統管理員。 若要更輕鬆地這麼做,您會在資料庫中建立檢視。 檢視只會包含下拉式清單所需的資料:完整名稱 (格式正確) 和記錄索引鍵。

[伺服器總管] 中,展開 [School.mdf],以滑鼠右鍵按一下 [ 檢視 ] 資料夾,然後選取 [ 新增檢視]。

Image06

當 [新增資料表] 對話方塊出現時,按一下 [關閉],並將下列 SQL 語句貼到 [SQL] 窗格中:

SELECT        LastName + ',' + FirstName AS FullName, PersonID
FROM          dbo.Person
WHERE        (HireDate IS NOT NULL)

將檢視儲存為 vInstructorName

更新資料模型

DAL 資料夾中,開啟 SchoolModel.edmx 檔案,以滑鼠右鍵按一下設計介面,然後 從資料庫選取 [更新模型]。

Image07

在 [ 選擇您的資料庫物件 ] 對話方塊中,選取 [ 新增 ] 索引標籤,然後選取您剛才建立的檢視。

Image08

按一下 [完成] 。

在設計工具中,您會看到工具已建立 vInstructorName 實體,以及 和 Person 實體之間的 Department 新關聯。

Image13

注意

在 [ 輸出錯誤清單 ] 視窗中,您可能會看到警告訊息,通知您工具會自動為新 vInstructorName 檢視建立主鍵。 這是正常的現象。

當您在程式碼中參考新的 vInstructorName 實體時,您不想使用前置詞小寫 「v」 的資料庫慣例。 因此,您會在模型中重新命名實體和實體集。

開啟 模型瀏覽器。 您會看到 vInstructorName 列為實體類型和檢視。

Image14

[SchoolModel (未 SchoolModel.Store) ] 底下,以滑鼠右鍵按一下 [vInstructorName ],然後選取 [ 屬性]。 在 [ 屬性 ] 視窗中,將 Name 屬性變更為 「InstructorName」,並將 [實體集名稱 ] 屬性變更為 「InstructorNames」。

Image15

儲存並關閉資料模型,然後重建專案。

使用存放庫類別和 ObjectDataSource 控制項

DAL 資料夾中建立新的類別檔案,將它命名為 SchoolRepository.cs,並以下列程式碼取代現有的程式碼:

using System;
using System.Collections.Generic;
using System.Linq;
using ContosoUniversity.DAL;

namespace ContosoUniversity.DAL
{
    public class SchoolRepository : IDisposable
    {
        private SchoolEntities context = new SchoolEntities();

        public IEnumerable<Department> GetDepartments()
        {
            return context.Departments.Include("Person").ToList();
        }

        private bool disposedValue = false;

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

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

    }
}

此程式碼提供單 GetDepartments 一方法,可傳回實體集中的所有實體 Departments 。 因為您知道您將存取傳回之每個資料列的 Person 導覽屬性,所以您會使用 Include 方法來指定該屬性的積極式載入。 類別也會實作 IDisposable 介面,以確保處置物件時會釋放資料庫連接。

注意

常見的作法是為每個實體類型建立存放庫類別。 在本教學課程中,會使用多個實體類型的一個存放庫類別。 如需存放庫模式的詳細資訊,請參閱 Entity Framework 小組部落格Julie Lerman 部落格中的文章。

方法 GetDepartments 會傳 IEnumerable 回 物件而非 IQueryable 物件,以確保即使處置存放庫物件本身之後,傳回的集合仍可使用。 IQueryable物件可能會在每次存取資料庫時造成資料庫存取,但當資料系結控制項嘗試轉譯資料時,存放庫物件可能會遭到處置。 您可以傳回另一個集合類型,例如 IList 物件,而不是 IEnumerable 物件。 不過,傳 IEnumerable 回 物件可確保您可以執行一般唯讀清單處理工作,例如 foreach 迴圈和 LINQ 查詢,但您無法在集合中加入或移除專案,這可能表示這類變更會保存到資料庫。

建立使用Site.Master 主版頁面的Departments.aspx頁面,並在名為 Content2Content 控制項中新增下列標記:

<h2>Departments</h2>
    <asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.DAL.SchoolRepository" 
        DataObjectTypeName="ContosoUniversity.DAL.Department" 
        SelectMethod="GetDepartments" >
    </asp:ObjectDataSource>
    <asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource"  >
        <Columns>
            <asp:CommandField ShowEditButton="True" ShowDeleteButton="True"
                ItemStyle-VerticalAlign="Top">
            </asp:CommandField>
            <asp:DynamicField DataField="Name" HeaderText="Name" SortExpression="Name" ItemStyle-VerticalAlign="Top" />
            <asp:DynamicField DataField="Budget" HeaderText="Budget" SortExpression="Budget" ItemStyle-VerticalAlign="Top" />
            <asp:DynamicField DataField="StartDate" HeaderText="Start Date" ItemStyle-VerticalAlign="Top" />
            <asp:TemplateField HeaderText="Administrator" SortExpression="Person.LastName" ItemStyle-VerticalAlign="Top" >
                <ItemTemplate>
                    <asp:Label ID="AdministratorLastNameLabel" runat="server" Text='<%# Eval("Person.LastName") %>'></asp:Label>,
                    <asp:Label ID="AdministratorFirstNameLabel" runat="server" Text='<%# Eval("Person.FirstMidName") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
        </Columns>
    </asp:GridView>

此標記會建立一個 ObjectDataSource 控制項,該控制項會使用您剛才建立的存放庫類別,以及 GridView 顯示資料的控制項。 控制項 GridView 會指定 [編輯 ] 和 [ 刪除 ] 命令,但您尚未新增程式碼來支援它們。

數個數據行會使用 DynamicField 控制項,讓您可以利用自動資料格式設定和驗證功能。 若要讓這些作業能夠運作,您必須在事件處理常式中 Page_Init 呼叫 EnableDynamicData 方法。 DynamicControl (控制項不會用於 Administrator 欄位中,因為它們不適用於導覽屬性。)

當您在方格中加入具有巢狀 GridView 控制項的資料行時,屬性 Vertical-Align="Top" 會變得很重要。

開啟 Departments.aspx.cs 檔案,並新增下列 using 語句:

using ContosoUniversity.DAL;

然後為頁面 Init 的事件新增下列處理常式:

protected void Page_Init(object sender, EventArgs e)
{
    DepartmentsGridView.EnableDynamicData(typeof(Department));
}

DAL 資料夾中,建立名為 Department.cs 的新類別檔案,並以下列程式碼取代現有的程式碼:

using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.DAL
{
    [MetadataType(typeof(DepartmentMetaData))]
    public partial class Department
    {
    }

    public class DepartmentMetaData
    {
        [DataType(DataType.Currency)]
        [Range(0, 1000000, ErrorMessage = "Budget must be less than $1,000,000.00")]
        public Decimal Budget { get; set; }

        [DisplayFormat(DataFormatString="{0:d}",ApplyFormatInEditMode=true)]
        public DateTime StartDate { get; set; }

    }
}

此程式碼會將中繼資料新增至資料模型。 它指定 Budget 實體的 Department 屬性實際上代表貨幣,但其資料類型為 Decimal ,而且指定值必須介於 0 到 $1,000,000.00 之間。 它也會指定 StartDate 屬性的格式應格式化為 mm/dd/yyyy 格式的日期。

執行 Departments.aspx 頁面。

顯示執行 [部門] 頁面的螢幕擷取畫面。

請注意,雖然您未在 BudgetStart Date資料行的Departments.aspx頁面標記中指定格式字串,但控制項已使用您在Department.cs檔案中提供的中繼資料來套用 DynamicField 預設貨幣和日期格式設定。

新增插入和刪除功能

開啟 SchoolRepository.cs,新增下列程式碼以建立 Insert 方法和 Delete 方法。 程式碼也包含名為 GenerateDepartmentID 的方法,可計算下一個可用的記錄索引鍵值以供 方法使用 Insert 。 這是必要的,因為資料庫未設定為自動計算資料表。 Department

public void InsertDepartment(Department department)
{
    try
    {
        department.DepartmentID = GenerateDepartmentID();
        context.Departments.AddObject(department);
        context.SaveChanges();
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

public void DeleteDepartment(Department department)
{
    try
    {
        context.Departments.Attach(department);
        context.Departments.DeleteObject(department);
        context.SaveChanges();
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

private Int32 GenerateDepartmentID()
{
    Int32 maxDepartmentID = 0;
    var department = (from d in GetDepartments()
                      orderby d.DepartmentID descending
                      select d).FirstOrDefault();
    if (department != null)
    {
        maxDepartmentID = department.DepartmentID + 1;
    }
    return maxDepartmentID;
}

Attach 方法

方法 DeleteDepartmentAttach 呼叫 方法,以重新建立在物件內容物件狀態管理員中,記憶體中實體與其所代表之資料庫資料列之間的連結。 這必須在方法呼叫 SaveChanges 方法之前發生。

物件內容一詞是指衍生自您用來存取實體集和實體之類別的 ObjectContext Entity Framework 類別。 在此專案的程式碼中,類別名為 SchoolEntities ,而且其實例一律名為 context 。 物件內容 的物件狀態管理員 是衍生自 類別的 ObjectStateManager 類別。 物件連絡人會使用物件狀態管理員來儲存實體物件,並追蹤每一個物件是否與資料庫中對應的資料表資料列或資料列同步。

當您讀取實體時,物件內容會將它儲存在物件狀態管理員中,並追蹤物件的表示是否與資料庫同步。 例如,如果您變更屬性值,則會設定旗標,指出您變更的屬性不再與資料庫同步。 然後,當您呼叫 SaveChanges 方法時,物件內容會知道資料庫中要做什麼,因為物件狀態管理員知道實體目前狀態與資料庫狀態之間的差異。

不過,此程式通常無法在 Web 應用程式中運作,因為讀取實體的物件內容實例及其物件狀態管理員中的所有內容,會在轉譯頁面之後處置。 必須套用變更的物件內容實例是具現化以供回傳處理的新實例。 在 方法的 DeleteDepartment 案例中 ObjectDataSource ,控制項會從檢視狀態的值重新建立實體的原始版本,但這個重新建立 Department 的實體不存在於物件狀態管理員中。 如果您在此重新建立的實體上呼叫 DeleteObject 方法,呼叫會失敗,因為物件內容不知道實體是否與資料庫同步。 不過,呼叫 Attach 方法會在重新建立的實體與資料庫中的值之間重新建立相同的追蹤,此追蹤原本是在物件內容先前實例中讀取實體時自動完成的值。

有時候,您不希望物件內容追蹤物件狀態管理員中的實體,而且您可以設定旗標來防止它執行此動作。 本系列稍後的教學課程將示範此範例。

SaveChanges 方法

這個簡單的存放庫類別說明如何執行 CRUD 作業的基本準則。 在此範例中 SaveChanges ,會在每次更新之後立即呼叫 方法。 在生產應用程式中,您可能會想要從個別的方法呼叫 SaveChanges 方法,讓您更充分掌控資料庫更新的時間。 (在下一個教學課程結束時,您會找到白皮書的連結,其中討論工作模式的單元,這是協調相關更新的方法之一。) 請注意,在此範例中, DeleteDepartment 方法不包含處理並行衝突的程式碼;在此系列稍後的教學課程中將新增該程式碼。

擷取講師名稱以在插入時選取

建立新部門時,使用者必須能夠從下拉式清單中的講師清單中選取系統管理員。 因此,將下列程式碼新增至 SchoolRepository.cs ,以使用您稍早建立的檢視來建立方法來擷取講師清單:

public IEnumerable<InstructorName> GetInstructorNames()
{
    return context.InstructorNames.OrderBy("it.FullName").ToList();
}

建立插入部門的頁面

建立使用Site.Master頁面的DepartmentsAdd.aspx頁面,並在名為 Content2Content 控制項中新增下列標記:

<h2>Departments</h2>
    <asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.DAL.SchoolRepository" DataObjectTypeName="ContosoUniversity.DAL.Department"
        InsertMethod="InsertDepartment" >
    </asp:ObjectDataSource>
    <asp:DetailsView ID="DepartmentsDetailsView" runat="server" 
        DataSourceID="DepartmentsObjectDataSource" AutoGenerateRows="False"
        DefaultMode="Insert" OnItemInserting="DepartmentsDetailsView_ItemInserting">
        <Fields>
            <asp:DynamicField DataField="Name" HeaderText="Name" />
            <asp:DynamicField DataField="Budget" HeaderText="Budget" />
            <asp:DynamicField DataField="StartDate" HeaderText="Start Date" />
            <asp:TemplateField HeaderText="Administrator">
                <InsertItemTemplate>
                    <asp:ObjectDataSource ID="InstructorsObjectDataSource" runat="server" 
                        TypeName="ContosoUniversity.DAL.SchoolRepository" 
                        DataObjectTypeName="ContosoUniversity.DAL.InstructorName"
                        SelectMethod="GetInstructorNames" >
                    </asp:ObjectDataSource>
                    <asp:DropDownList ID="InstructorsDropDownList" runat="server" 
                        DataSourceID="InstructorsObjectDataSource"
                        DataTextField="FullName" DataValueField="PersonID" OnInit="DepartmentsDropDownList_Init">
                    </asp:DropDownList>
                </InsertItemTemplate>
            </asp:TemplateField>
            <asp:CommandField ShowInsertButton="True" />
        </Fields>
    </asp:DetailsView>
   <asp:ValidationSummary ID="DepartmentsValidationSummary" runat="server" 
        ShowSummary="true" DisplayMode="BulletList"  />

此標記會建立兩 ObjectDataSource 個控制項,一個用於插入新的 Department 實體,另一個用於擷取用於選取部門系統管理員之控制項的 DropDownList 講師名稱。 標記會 DetailsView 建立控制項來輸入新的部門,並指定控制項 ItemInserting 事件的處理常式,以便您可以設定 Administrator 外鍵值。 最後是 ValidationSummary 顯示錯誤訊息的控制項。

開啟 DepartmentsAdd.aspx.cs 並新增下列 using 語句:

using ContosoUniversity.DAL;

新增下列類別變數和方法:

private DropDownList administratorsDropDownList;

protected void Page_Init(object sender, EventArgs e)
{
    DepartmentsDetailsView.EnableDynamicData(typeof(Department));
}

protected void DepartmentsDropDownList_Init(object sender, EventArgs e)
{
    administratorsDropDownList = sender as DropDownList;
}

protected void DepartmentsDetailsView_ItemInserting(object sender, DetailsViewInsertEventArgs e)
{
    e.Values["Administrator"] = administratorsDropDownList.SelectedValue;
}

方法 Page_Init 會啟用動態資料功能。 控制項事件的處理常式 DropDownList 會儲存控制項的參考,而控制項 Inserting 事件的處理常式 DetailsView 會使用該參考來取得 PersonID 所選講師的值,並更新 Administrator 實體的 Department 外鍵 Init 屬性。

執行頁面、新增新部門的資訊,然後按一下 [插入 ] 連結。

Image04

輸入另一個新部門的值。 在 [ 預算 ] 欄位中輸入大於 1,000,000.00 的數位,然後在下一個欄位輸入索引標籤。 星號會出現在欄位中,如果您將滑鼠指標放在該欄位上方,您會看到您在該欄位的中繼資料中輸入的錯誤訊息。

Image03

按一下 [插入],您會看到頁面底部控制項所顯示 ValidationSummary 的錯誤訊息。

Image12

接下來,關閉瀏覽器並開啟 Departments.aspx 頁面。 將屬性新增至控制項,並將 DataKeyNames 屬性 GridView 新增至控制項,將刪除功能新增 DeleteMethodObjectDataSourceDepartments.aspx頁面。 這些控制項的開頭標記現在會類似下列範例:

<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.DAL.SchoolRepository" 
        DataObjectTypeName="ContosoUniversity.DAL.Department"
        SelectMethod="GetDepartments" 
        DeleteMethod="DeleteDepartment" >

    <asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource" DataKeyNames="DepartmentID" >

執行頁面。

顯示 [部門] 頁面執行之後的螢幕擷取畫面。

刪除您在執行 DepartmentsAdd.aspx 頁面時新增的部門。

新增更新功能

開啟 SchoolRepository.cs 並新增下列 Update 方法:

public void UpdateDepartment(Department department, Department origDepartment)
{
    try
    {
        context.Departments.Attach(origDepartment);
        context.ApplyCurrentValues("Departments", department);
        context.SaveChanges();
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

當您在Departments.aspx頁面中按一下[更新] 時, ObjectDataSource 控制項會建立兩 Department 個實體以傳遞至 UpdateDepartment 方法。 其中一個包含已儲存在檢視狀態的原始值,另一個值則包含控制項中 GridView 輸入的新值。 方法中的 UpdateDepartment 程式碼會將具有原始值的實體傳遞 DepartmentAttach 方法,以建立實體與資料庫中專案的追蹤。 然後,程式碼會將 Department 具有新值的實體傳遞至 ApplyCurrentValues 方法。 物件內容會比較舊值和新值。 如果新值與舊值不同,則物件內容會變更屬性值。 SaveChanges方法接著只會更新資料庫中已變更的資料行。 不過, (,如果此實體的更新函式對應至預存程式,則不論變更的資料行為何,都會更新整個資料列。)

開啟 Departments.aspx 檔案,並將下列屬性新增至 DepartmentsObjectDataSource 控制項:

  • UpdateMethod="UpdateDepartment"
  • ConflictDetection="CompareAllValues"
    這會導致舊值儲存在檢視狀態,以便與 方法中的 Update 新值進行比較。
  • OldValuesParameterFormatString="orig{0}"
    這會通知控制項原始值參數的名稱為 origDepartment

控制項開頭標記的 ObjectDataSource 標記現在類似下列範例:

<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.DAL.SchoolRepository" 
        DataObjectTypeName="ContosoUniversity.DAL.Department" 
        SelectMethod="GetDepartments" DeleteMethod="DeleteDepartment" 
        UpdateMethod="UpdateDepartment"
        ConflictDetection="CompareAllValues" 
        OldValuesParameterFormatString="orig{0}" >

OnRowUpdating="DepartmentsGridView_RowUpdating"將屬性新增至 GridView 控制項。 您將使用此專案,根據使用者在下拉式清單中選取的資料列來設定 Administrator 屬性值。 開頭 GridView 標記現在類似下列範例:

<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource" DataKeyNames="DepartmentID"
        OnRowUpdating="DepartmentsGridView_RowUpdating">

EditItemTemplate將資料行的 Administrator 控制項新增至 GridView 控制項,緊接在該資料行的 ItemTemplate 控制項之後:

<EditItemTemplate>
                    <asp:ObjectDataSource ID="InstructorsObjectDataSource" runat="server" DataObjectTypeName="ContosoUniversity.DAL.InstructorName"
                        SelectMethod="GetInstructorNames" TypeName="ContosoUniversity.DAL.SchoolRepository">
                    </asp:ObjectDataSource>
                    <asp:DropDownList ID="InstructorsDropDownList" runat="server" DataSourceID="InstructorsObjectDataSource"
                        SelectedValue='<%# Eval("Administrator")  %>'
                        DataTextField="FullName" DataValueField="PersonID" OnInit="DepartmentsDropDownList_Init" >
                    </asp:DropDownList>
                </EditItemTemplate>

EditItemTemplate 控制項類似于 InsertItemTemplateDepartmentsAdd.aspx 頁面中的控制項。 差異在於,控制項的初始值是使用 SelectedValue 屬性來設定。

GridView 控制項之前,如您在 DepartmentsAdd.aspx頁面中所做的一樣新增 ValidationSummary 控制項。

<asp:ValidationSummary ID="DepartmentsValidationSummary" runat="server" 
        ShowSummary="true" DisplayMode="BulletList"  />

開啟 Departments.aspx.cs ,並在部分類別宣告之後立即新增下列程式碼,以建立私用欄位以參考 DropDownList 控制項:

private DropDownList administratorsDropDownList;

然後新增 DropDownList 控制項 Init 事件和 GridView 控制項事件的 RowUpdating 處理常式:

protected void DepartmentsDropDownList_Init(object sender, EventArgs e)
{
    administratorsDropDownList = sender as DropDownList;
}

protected void DepartmentsGridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
    e.NewValues["Administrator"] = administratorsDropDownList.SelectedValue;
}

事件的處理常式會將 Init 控制項的 DropDownList 參考儲存在類別欄位中。 事件的處理常式 RowUpdating 會使用 參考來取得使用者輸入的值,並將其套用至 Administrator 實體的 Department 屬性。

使用 DepartmentsAdd.aspx 頁面來新增部門,然後執行 Departments.aspx 頁面,然後按一下您新增的資料列上的 [編輯 ]。

注意

因為資料庫中的資料無效,所以您無法編輯您未新增 (的資料列,也就是已經存在於資料庫) 中;使用資料庫建立之資料列的系統管理員是學生。 如果您嘗試編輯其中一個錯誤,您會收到回報類似錯誤的錯誤頁面 'InstructorsDropDownList' has a SelectedValue which is invalid because it does not exist in the list of items.

Image10

如果您輸入無效 的預算 金額,然後按一下 [ 更新],您會看到您在 Departments.aspx 頁面中看到的相同星號和錯誤訊息。

變更域值,或選取不同的系統管理員,然後按一下 [ 更新]。 隨即顯示變更。

顯示 [部門] 頁面的螢幕擷取畫面。

這會完成使用 ObjectDataSource 基本 CRUD (建立、讀取、更新、刪除) 作業與 Entity Framework 的控制項簡介。 您已建置簡單的多層式應用程式,但商務邏輯層仍與資料存取層緊密結合,這會使自動化單元測試複雜。 在下列教學課程中,您將瞭解如何實作存放庫模式來加速單元測試。