使用scaffold功能設計Blazor應用程式- 2
作者:許薰尹
本篇文章將延續本站《使用scaffold功能設計Blazor應用程式 - 1》一文的情境,介紹如何使用Visual Studio 2022的Scaffolding功能建立Blazor CRUD頁面,利用工具的圖型介面進行資料移轉以及說明自動產生的程式碼用途。
使用EFC資料移轉建立資料庫
Visual Studio 2022提供一個圖型介面,不必記憶或輸入指令,就可以使用EFC資料移轉功能來建立資料庫。從「Solution Explorer」視窗 >專案名稱下方「Connected Services」項目上按滑鼠右鍵,從快捷選單選擇「Manage Connected Services」選項,請參考下圖所示:
圖 1:「Manage Connected Services」選項。
點選開啟的畫面「SQL Server Express LocalDB (local)」項目右方的「…」按鈕,從快捷選單選擇「Add Migraiton」選項,請參考下圖所示:
圖 2:使用EFC移轉 (EFC Migration)。
接下來會看到「Entity Frameowk Migration」視窗,在「Migration name」項目設定一個名稱,「DbContext class names」項目選取「BlazorScaffold.Data .BlazorScaffoldContext」,然後按「Finish」按鈕,請參考下圖所示:
圖 3:設定使用EFC移轉名稱。
接著按「Close」按鈕結束「Entity Frameowk Migration」畫面,請參考下圖所示:
圖 4:關閉「Entity Frameowk Migration」視窗。
Visual Studio 2022工具會自動在專案中產生一個「Migrations」資料夾,其中包含C#程式碼用來建立資料庫,請參考下圖所示:
圖 5:「Migrations」資料夾。
點選開啟的「Manage Connected Services」畫面,在「SQL Server Express LocalDB (local)」項目右方的「…」按鈕,從快捷選單選擇「Update Database」選項更新資料庫,請參考下圖所示:
圖 6:使用EFC移轉更新資料庫。
接著按「Finish」按鈕結束「Entity Frameowk Migration」畫面,請參考下圖所示:
圖 7:更新資料庫。
接著按「Close」按鈕結束「Entity Frameowk Migration」畫面,請參考下圖所示:
圖 8:關閉「Entity Frameowk Migration」視窗。
在Visual Studio 2022開發工具,按CTRL+F5執行網站,執行結果參考如下,選取「Employees」選項可以看到圖書清單的畫面,因為資料庫剛建立清單內容為空:
圖 9:「Index.razor」執行結果。
Visual Studio 2022開發工具會在「Pages」資料夾下建立一個「EmployeesPages」資料夾,其中包含CRUD操作頁面,分別說如下。
Index頁面
「Index」頁面的程式參考如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | @page "/employees" @ using Microsoft.AspNetCore.Components.QuickGrid @inject BlazorScaffold.Data.BlazorScaffoldContext DB @ using BlazorScaffold.Models <PageTitle> Index </PageTitle> <h1> Index </h1> <p> <a href = "employees/create" > Create New </a> </p> <QuickGrid Class = "table" Items = "DB.Employees" > <PropertyColumn Property = "employee => employee.EmployeeName" /> <PropertyColumn Property = "employee => employee.BirthDay" /> <PropertyColumn Property = "employee => employee.IsMarried" /> <PropertyColumn Property = "employee => employee.Department" /> <TemplateColumn Context = "employee" > <a href = "@($" employees/edit?employeeid={employee.EmployeeId} ")" > Edit </a> | <a href = "@($" employees/details?employeeid={employee.EmployeeId} ")" > Details </a> | <a href = "@($" employees/delete?employeeid={employee.EmployeeId} ")" > Delete </a> </TemplateColumn> </QuickGrid> |
「Index.razor」用於顯示員工資料的頁面。首先,我們可以看到 「@page "/employees"」這行程式碼,表示這個元件的URL路由為「/employees」,當使用者在瀏覽器中輸入 /employees 時,這個元件將會顯示在瀏覽器中。
接下來,「@using」語句引入「Microsoft.AspNetCore.Components.QuickGrid」命名空間,「QuickGrid」是一個用於快速建立資料表格的元件。接著使用「@inject」指示詞透過相依性插入取得「BlazorScaffold.Data.BlazorScaffoldContext」物件用於存取資料庫。
「Create New」是一個超連結,它指向了「/employees/create」的 URL,這個連結用於導覽到建立新員工的「Create.razor」頁面。
在「QuickGrid」元件中,我們設定了一個 CSS 類別為「table」,這個類別來自於「Bootstrap」套件。將「Items」屬性設定為「DB.Employees」,這表示我們將使用表格來顯示「DB.Employees」中的資料。
在「QuickGrid」元件中,我們使用了「PropertyColumn」元件來定義資料表格的欄位。每個 「PropertyColumn」元件都有一個「Property」屬性,用於指定要顯示的屬性。在這個例子中,我們顯示了員工的姓名(EmployeeName)、生日(BirthDay)、婚姻狀態(IsMarried)和部門(Department)。
最後,使用了「TemplateColumn」元件來定義一個自訂的欄位。在這個欄位中,我們使用了 Razor 語法來動態產生超連結,這些連結分別指向編輯(Edit)、詳細資料(Details)和刪除(Delete)員工的頁面。每個連結都使用查詢字串傳遞了員工編號(EmployeeId)。
Create頁面
點選「Index」頁面上的「Create New」連結會顯示「Create.razor」以便新增資料,程式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | @page "/employees/create" @inject BlazorScaffold.Data.BlazorScaffoldContext DB @ using BlazorScaffold.Models @inject NavigationManager NavigationManager <PageTitle> Create </PageTitle> <h1> Create </h1> <h4> Employee </h4> <hr /> <div class = "row" > <div class = "col-md-4" > <EditForm method = "post" Model = "Employee" OnValidSubmit = "AddEmployee" FormName = "create" Enhance> <DataAnnotationsValidator /> <ValidationSummary class = "text-danger" /> <div class = "mb-3" > <label for = "employeename" class = "form-label" > EmployeeName: </label> <InputText id = "employeename" @bind-Value = "Employee.EmployeeName" class = "form-control" /> <ValidationMessage For = "() => Employee.EmployeeName" class = "text-danger" /> </div> <div class = "mb-3" > <label for = "birthday" class = "form-label" > BirthDay: </label> <InputDate id = "birthday" @bind-Value = "Employee.BirthDay" class = "form-control" /> <ValidationMessage For = "() => Employee.BirthDay" class = "text-danger" /> </div> <div class = "mb-3" > <label for = "ismarried" class = "form-label" > IsMarried: </label> <InputCheckbox id = "ismarried" @bind-Value = "Employee.IsMarried" class = "form-check-input" /> <ValidationMessage For = "() => Employee.IsMarried" class = "text-danger" /> </div> <div class = "mb-3" > <label for = "department" class = "form-label" > Department: </label> <InputText id = "department" @bind-Value = "Employee.Department" class = "form-control" /> <ValidationMessage For = "() => Employee.Department" class = "text-danger" /> </div> <button type = "submit" class = "btn btn-primary" > Create </button> </EditForm> </div> </div> <div> <a href = "/employees" > Back to List </a> </div> @code { [SupplyParameterFromForm] public Employee Employee { get ; set ; } = new (); // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD public async Task AddEmployee() { DB.Employees.Add( Employee ); await DB.SaveChangesAsync(); NavigationManager.NavigateTo( "/employees" ); } } |
「Create」元件於建立新的員工資料。它包含了一個表單(EditForm),讓使用者輸入員工的相關資訊,並在提交表單後將資料新增到資料庫中。
和「Index」元件一樣使用「@inject」指示詞透過相依性插入取得「BlazorScaffold.Data.BlazorScaffoldContext」用於存取資料庫;而「NavigationManager」用於導覽到其他頁面。
「EditForm」是 Blazor 的內建元件,用於建立表單。它的「Model」屬性繫結到「Employee」物件,表示表單的資料模型是「Employee」型別。「OnValidSubmit」屬性指定了當表單驗證通過且提交時要執行的方法,這裡是「AddEmployee」方法。「FormName」屬性設定了表單的名稱為「create」。
「EditForm」的「Enhance」屬性的值設定為「True」,這表示表單提交時,不會完全重新載入頁面,但這個設定只適用於伺服器端轉譯 (SSR)的情境。
在「EditForm」元件內部,使用「label」顯示欄位的標籤,「InputText」、「InputDate」 和 「InputCheckbox」元件用於輸入文字、日期、布林值等不同類型的輸入方塊。「@bind-Value」屬性用於將輸入方塊的值繫結到「Employee」物件的對應屬性。「ValidationMessage」元件用於顯示驗證錯誤訊息。
最後,「button」按鈕用於提交表單,並叫用「AddEmployee」方法新增資料到資料庫中。
在「@code」程式區塊中,定義了「Employee」屬性,並使用 「SupplyParameterFromForm」屬性來指示 Blazor 從表單中將欄位值填到「Employee」物件對應的屬性。「AddEmployee」方法則利用Entity Framework Core將「Employee」物件新增到資料庫中,再透過「NavigationManager」導「NavigationManager」到員工列表頁面。
這個元件的執行結果請參考下圖所示:
圖 10:資料新增。
新增資料後回到員工列表頁面,請參考下圖所示:
圖 11:新增資料後回到員工列表。
Details頁面
點選員工列表(Index)中任一筆資料的「Details」連結會導覽到「Details.razor」元件,程式參考如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | @page "/employees/details" @inject BlazorScaffold.Data.BlazorScaffoldContext DB @ using BlazorScaffold.Models @inject NavigationManager NavigationManager @ using Microsoft.EntityFrameworkCore <PageTitle> Details </PageTitle> <h1> Details </h1> <div> <h4> Employee </h4> <hr /> @ if ( employee is null ) { <p><em> Loading... </em></p> } else { <dl class = "row" > <dt class = "col-sm-2" > EmployeeName </dt> <dd class = "col-sm-10" > @employee.EmployeeName </dd> <dt class = "col-sm-2" > BirthDay </dt> <dd class = "col-sm-10" > @employee.BirthDay </dd> <dt class = "col-sm-2" > IsMarried</dt> <dd class = "col-sm-10" > @employee.IsMarried </dd> <dt class = "col-sm-2" > Department </dt> <dd class = "col-sm-10" > @employee.Department </dd> </dl> <div> <a href = "@($" /employees/edit?employeeid={employee.EmployeeId} ")" > Edit </a> | <a href = "@($" /employees ")" > Back to List </a> </div> } </div> @code { Employee? employee; [SupplyParameterFromQuery] public int EmployeeId { get ; set ; } protected override async Task OnInitializedAsync() { employee = await DB.Employees.FirstOrDefaultAsync( m => m.EmployeeId == EmployeeId ); if ( employee is null ) { NavigationManager.NavigateTo( "notfound" ); } } } |
「「Details.razor」元件用於顯示員工詳細資訊的頁面。元件使用了條件語句「@if (employee is null)」來檢查員工是否為空值。如果員工為空值,則顯示「Loading」的訊息;否則,顯示員工的詳細資訊。
元件使用了「dl」和「dt」、「dd」 HTML元素來顯示員工的姓名(EmployeeName)、生日(BirthDay)、婚姻狀態(IsMarried)和部門(Department)。
「@code」程式碼區塊包含了元件的程式邏輯。它定義了一個可為空值的「employee」變數,並使用 「SupplyParameterFromQuery」 屬性將「EmployeeId」參數從查詢字串繫結到這個變數。
「OnInitializedAsync」方法使用「DB.Employees.FirstOrDefaultAsync」方法從資料庫中查詢員工資訊並將結果放到「employee」變數。如果員工為空值,則使用「NavigationManager.NavigateTo」方法導覽到「notfound」頁面。
這個元件執行結果請參考下圖所示:
圖 12:「Details」元件。
Edit頁面
「Edit.razor」元件用來編輯員工資料,程式參考如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | @page "/Employees/edit" @inject BlazorScaffold.Data.BlazorScaffoldContext DB @ using BlazorScaffold.Models @inject NavigationManager NavigationManager @ using Microsoft.EntityFrameworkCore <PageTitle> Edit </PageTitle> <h1> Edit </h1> <h4> Employee </h4> <hr /> @ if ( Employee is null ) { <p><em> Loading... </em></p> } else { <div class = "row" > <div class = "col-md-4" > <EditForm method = "post" Model = "Employee" OnValidSubmit = "UpdateEmployee" FormName = "edit" Enhance> <DataAnnotationsValidator /> <ValidationSummary /> <input type = "hidden" name = "Employee.EmployeeId" value = "@Employee.EmployeeId" /> <div class = "mb-3" > <label for = "employeename" class = "form-label" > EmployeeName: </label> <InputText id = "employeename" @bind-Value = "Employee.EmployeeName" class = "form-control" /> <ValidationMessage For = "() => Employee.EmployeeName" class = "text-danger" /> </div> <div class = "mb-3" > <label for = "birthday" class = "form-label" > BirthDay: </label> <InputDate id = "birthday" @bind-Value = "Employee.BirthDay" class = "form-control" /> <ValidationMessage For = "() => Employee.BirthDay" class = "text-danger" /> </div> <div class = "mb-3" > <label for = "ismarried" class = "form-label" > IsMarried: </label> <InputCheckbox id = "ismarried" @bind-Value= "Employee.IsMarried" class = "form-check-input" /> <ValidationMessage For = "() => Employee.IsMarried" class = "text-danger" /> </div> <div class = "mb-3" > <label for = "department" class = "form-label" > Department: </label> <InputText id = "department" @bind-Value = "Employee.Department" class = "form-control" /> <ValidationMessage For = "() => Employee.Department" class = "text-danger" /> </div> <button type = "submit" class = "btn btn-primary" > Save </button> </EditForm> </div> </div> } <div> <a href = "/employees" >Back to List</a> </div> @code { [SupplyParameterFromQuery] public int EmployeeId { get ; set ; } [SupplyParameterFromForm] public Employee? Employee { get ; set ; } protected override async Task OnInitializedAsync() { Employee ??= await DB.Employees.FirstOrDefaultAsync( m => m.EmployeeId == EmployeeId ); if ( Employee is null ) { NavigationManager.NavigateTo( "notfound" ); } } // To protect from overposting attacks, enable the specific properties you want to bind to. // For more details, see https://aka.ms/RazorPagesCRUD. public async Task UpdateEmployee() { DB.Attach( Employee! ).State = EntityState.Modified; try { await DB.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if ( !EmployeeExists( Employee!.EmployeeId ) ) { NavigationManager.NavigateTo( "notfound" ); } else { throw ; } } NavigationManager.NavigateTo( "/employees" ); } bool EmployeeExists( int employeeid ) { return DB.Employees.Any( e => e.EmployeeId == employeeid ); } } |
「Edit.razor」元件用於編輯員工資料,程式的HTML樣板結構和「Create」元件雷同,包含了一個表單,可以顯示和編輯員工的姓名(EmployeeName)、生日(BirthDay)、婚姻狀態(IsMarried)和部門(Department)。
在「@code」程式碼區塊中定義了一個「EmployeeId」屬性,並使用 「SupplyParameterFromQuery」 屬性來從查詢參數中獲取員工的 ID。同樣地定義了一個可為空值的「Employee」屬性,並使用 「SupplyParameterFromForm」屬性來從表單中獲取員工的資料。
在「OnInitializedAsync」方法中使用「DB.Employees.FirstOrDefaultAsync」方法從資料庫中獲取指定 ID 的員工資料,並將其放到「Employee」屬性。如果找不到員工,則導覽到「notfound」頁面。
在表單的「OnValidSubmit」事件叫用「UpdateEmployee」方法,將「Employee」物件的狀態設定為「EntityState.Modified」,表示該員工資料已被修改。然後使用「DB.SaveChangesAsync」方法將修改後的員工資料保存到資料庫之中。如果在儲存資料的過程中發生「DbUpdateConcurrencyException」例外錯誤,便檢查員工是否仍存在於資料庫之中,如果不存在,則導覽到「notfound」頁面。最後,無論保存是否成功,它都導覽回到「/employees」頁面。
這個元件執行結果請參考下圖所示:
圖 13:資料修改。
Delete頁面
「Delete.razor」元件用來確認刪除員工資料,程式參考如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | @page "/employees/delete" @inject BlazorScaffold.Data.BlazorScaffoldContext DB @ using BlazorScaffold.Models @inject NavigationManager NavigationManager @ using Microsoft.EntityFrameworkCore <PageTitle> Delete </PageTitle> <h1> Delete </h1> <h3> Are you sure you want to delete this ? </h3> <div> <h4> Employee </h4> <hr /> @ if ( employee is null ) { <p><em> Loading... </em></p> } else { <dl class = "row" > <dt class = "col-sm-2" > EmployeeName </dt> <dd class = "col-sm-10" > @employee.EmployeeName </dd> </dl> <dl class = "row" > <dt class = "col-sm-2" > BirthDay </dt> <dd class = "col-sm-10" > @employee.BirthDay </dd> </dl> <dl class = "row" > <dt class = "col-sm-2" > IsMarried</dt> <dd class = "col-sm-10" > @employee.IsMarried </dd> </dl> <dl class = "row" > <dt class = "col-sm-2" > Department </dt> <dd class = "col-sm-10" > @employee.Department </dd> </dl> <EditForm method = "post" Model = "employee" OnValidSubmit = "DeleteEmployee" FormName = "delete" Enhance> <button type = "submit" class = "btn btn-danger" disabled = "@(employee is null)" > Delete </button> | <a href = "/employees" > Back to List </a> </EditForm> } </div> @code { Employee? employee; [SupplyParameterFromQuery] public int EmployeeId { get ; set ; } protected override async Task OnInitializedAsync() { employee = await DB.Employees.FirstOrDefaultAsync( m => m.EmployeeId == EmployeeId ); if ( employee is null ) { NavigationManager.NavigateTo( "notfound" ); } } public async Task DeleteEmployee() { DB.Employees.Remove( employee! ); await DB.SaveChangesAsync(); NavigationManager.NavigateTo( "/employees" ); } } |
「Delete.razor」元件用刪除資料的操作,它提供了一個用於刪除員工的介面,並使用「BlazorScaffoldContext」類別來存取資料庫。這個元件上半部程式碼顯示了員工的詳細資訊,包含姓名(EmployeeName)、生日(BirthDay)、婚姻狀態(IsMarried)和部門(Department)。
「Delete.razor」元件下半段程式碼則使用「EditForm」元件來建立一個表單,該表單的「Model」屬性繫結到「employee」變數,並在提交表單時叫用「DeleteEmployee」方法從資料庫中刪除員工,並在刪除完成後導覽回 "/employees" 頁面。
這個元件執行結果請參考下圖所示:
圖 14:刪除員工資料。
0 意見:
張貼留言