ASP.NET Core Blazor 主/明细 CRUD(含过滤和排序)结合 EF 和 Web API






4.79/5 (26投票s)
在本文中,我们将了解如何使用 Entity Framework 和 Web API 为主/明细网格创建自定义的 ASP.NET Core Blazor 主/明细 HTML CRUD(插入、更新、选择和删除),并支持排序和过滤。
引言
在本文中,我们将了解如何使用 Entity Framework 和 Web API 为主/明细网格创建自定义的 ASP.NET Core Blazor 主/明细 HTML CRUD(插入
、更新
、选择
和删除
),并支持排序和过滤。
请阅读我之前的文章,其中深入介绍了 ASP.NET Core Blazor 的入门知识。
- 使用 Entity Framework 和 Web API 实现 ASP.NET Core Blazor CRUD
- 使用 Entity Framework 和 Web API 实现 ASP.NET Core Blazor 筛选和排序
- 使用 EF 和 Web API 实现 ASP.NET Core Blazor 主从表格(含筛选和排序)
本文将讲解
- 如何创建订单主表和订单明细表并插入示例记录
- 如何创建 ASP.NET Core Blazor Web 应用程序
- 如何安装用于 Entity Framework 和创建 DBContext 类的包
- 为订单主表和订单明细表创建单独的 Web API
- 如何从 Web API 获取结果并在 Blazor 客户端 Razor 视图中绑定结果以显示主/明细 HTML 网格
- 为主 HTML 网格添加排序/过滤功能
- 从网格中添加/编辑/删除每个订单主表和订单明细
背景
必备组件
请确保您的计算机已安装所有先决条件。如果没有,请逐一下载并安装所有。请注意,由于 Blazor 是一个新框架,我们必须安装 Visual Studio 2017 (15.7) 或更高版本的预览版。
- 安装 .NET Core 2.1 预览版 2 SDK。(您可以在此 链接中找到所有版本)
- 安装 Visual Studio 2017 (15.7) 的最新预览版。
- 安装 ASP.NET Core Blazor 语言服务 以获得 Blazor 扩展。
Using the Code
我们将创建一个 Order
Master
(主表)和 Order Detail
(明细表)来用于 Master
和 Detail Grid
(主/明细网格)的数据绑定。以下脚本用于创建数据库、表和示例插入查询。在 SQL Server 中运行此脚本。
use master
--create DataBase
-- 1) Check for the Database Exists .If the database is exist then drop and create new DB
IF EXISTS (SELECT [name] FROM sys.databases WHERE [name] = 'OrderManagement' )
DROP DATABASE OrderManagement
GO
CREATE DATABASE OrderManagement
GO
USE OrderManagement
GO
-- Create OrderMasters Table
CREATE TABLE [dbo].[OrderMasters](
[Order_No] INT IDENTITY PRIMARY KEY,
[Table_ID] [varchar](20) NOT NULL,
[Description] [varchar](200) NOT NULL,
[Order_DATE] [datetime] NOT NULL,
[Waiter_Name] [varchar](20) NOT NULL
)
-- Insert OrderMasters sample data
INSERT INTO [OrderMasters]
([Table_ID] ,[Description],[Order_DATE],[Waiter_Name])
VALUES
('T1','Order for Table T1',GETDATE(),'SHANU' )
INSERT INTO [OrderMasters]
([Table_ID] ,[Description],[Order_DATE],[Waiter_Name])
VALUES
('T2','Order for Table T2',GETDATE(),'Afraz' )
INSERT INTO [OrderMasters]
([Table_ID] ,[Description],[Order_DATE],[Waiter_Name])
VALUES
('T3','Order for Table T3',GETDATE(),'Afreen')
CREATE TABLE [dbo].[OrderDetails](
[Order_Detail_No] INT IDENTITY PRIMARY KEY,
[Order_No] INT,
[Item_Name] [varchar](20) NOT NULL,
[Notes] [varchar](200) NOT NULL,
[QTY] INT NOT NULL,
[Price] INT NOT NULL
)
--Now let’s insert the 3 items for the above Order No 'Ord_001'.
INSERT INTO [OrderDetails]
( [Order_No],[Item_Name],[Notes],[QTY] ,[Price])
VALUES
(1,'Ice Cream','Need very Cold',2 ,160)
INSERT INTO [OrderDetails]
([Order_No],[Item_Name],[Notes],[QTY] ,[Price])
VALUES
(1,'Coffee','Hot and more Suger',1 ,80)
INSERT INTO [OrderDetails]
([Order_No],[Item_Name],[Notes],[QTY] ,[Price])
VALUES
(1,'Burger','Spicy',3 ,140)
INSERT INTO [OrderDetails]
([Order_No],[Item_Name],[Notes],[QTY] ,[Price])
VALUES
(2,'Pizza','More Chees and Large',1 ,350)
INSERT INTO [OrderDetails]
([Order_No],[Item_Name],[Notes],[QTY] ,[Price])
VALUES
(2,'Cola','Need very Cold',3 ,50)
INSERT INTO [OrderDetails]
([Order_No],[Item_Name],[Notes],[QTY] ,[Price])
VALUES
(3,'IDLY','Hot',3 ,40)
INSERT INTO [OrderDetails]
([Order_No],[Item_Name],[Notes],[QTY] ,[Price])
VALUES
(3,'Thosa','Hot',3 ,50)
-- To Select and test Order Master and Details
Select * FROM OrderMasters
Select * From OrderDetails
步骤 2 - 创建 ASP.NET Core Blazor 应用程序
在安装了上述所有先决条件和 ASP.NET Core Blazor 语言服务后,请在桌面上点击开始 >> 程序 >> Visual Studio 2017 >> Visual Studio 2017。点击新建 >> 项目。选择 Web >> ASP.NET Core Angular Web 应用程序。输入您的项目名称,然后点击确定。
选择 Blazor (ASP.NET Core hosted),然后单击“确定”。
创建 ASP.NET Core Blazor 应用程序后,请稍等片刻。您将在解决方案资源管理器中看到以下结构。
ASP.NET Core Blazor 解决方案有什么新内容?
当我们创建新的 ASP.NET Core Blazor 应用程序时,我们可以看到解决方案资源管理器中会自动创建 3 个项目。
客户端项目
创建的第一个项目是客户端项目,它将是我们的 Solutionname.Client
,在这里,我们将 Solutionname
视为“BlazorASPCORE
”。由于该项目命名为客户端,因此它主要专注于所有客户端视图。在这里,我们将添加所有将在客户端浏览器中显示的页面视图。
我们可以看到这里已经添加了一些示例页面,还可以看到一个共享文件夹,它与我们的 MVC 应用程序相同,其中包含 Sharedfolder
和 Layout
页面用于 Master
页面。在 Blazor 中,我们有 MainLayout
,它将用作 Master
页面,NavMenu
用于左侧菜单显示。
服务器项目
顾名思义,该项目将用作服务器项目。该项目主要用于创建所有 Controller 和 WEB API Controller,以使用 WEB API 执行所有业务逻辑和 CRUD 操作。在我们的演示应用程序中,我们将在该服务器项目中添加一个 Web API,并在我们的客户端应用程序中添加所有 Web API。该服务器项目将充当 get
/set
数据库中的数据,而我们的 Client
项目则将绑定或发送结果到该服务器以在数据库中执行 CRUD 操作。
共享项目
顾名思义,该项目用作共享项目。该项目充当我们的服务器项目和客户端项目的模型。在此共享项目中声明的模型将在服务器项目和客户端项目中使用。我们还将所有项目所需的包安装在此共享项目中,例如,要使用 Entity Framework,我们就将所有包安装在此共享项目中。
运行以测试应用程序
当我们运行应用程序时,我们可以看到左侧是导航,右侧是数据。我们可以看到默认的示例页面和菜单将显示在我们的 Blazor 网站中。我们可以使用这些页面,也可以删除它们并开始创建自己的页面。
现在,让我们看看如何添加新页面来执行 CRUD 操作以维护 Order
详细信息。
使用 Entity Framework
要在我们的 Blazor 应用程序中使用 Entity Framework,我们需要安装以下包。
安装包
转到“工具”,然后选择 ->“NuGet 包管理器” ->“程序包管理器控制台”。
您可以在 VS 2017 IDE 的底部看到控制台,在控制台组合框的右侧,将默认项目选择为您的共享项目“Select Shared”。
- 您将看到 PM>,然后复制并粘贴下面的行以安装数据库提供程序包。此包用于将数据库提供程序设置为 SQL Server。
Install-Package Microsoft.EntityFrameworkCore.SqlServer
我们可以看到包已安装在我们的共享文件夹中。
安装 Entity Framework。
您将看到 PM>,然后复制并粘贴下面的行以安装 EF 包。
Install-Package Microsoft.EntityFrameworkCore.Tools
创建 DB Context 并设置 DB 连接字符串
您会看到 PM>,然后复制并粘贴以下行来设置连接字符串并创建 DB Context。这是重要部分,因为我们提供了 SQL Server 名称、数据库名称、SQL Server UID 和 SQL Server 密码来连接到我们的数据库以显示我们的主/明细网格。我们还提供了两个 SQL 表名“OrderMasters
, OrderDetails
”来在我们的共享项目中创建 Model
类。
Scaffold-DbContext "Server= SQLServerName;Database=OrderManagement;
user id=SQLID;password=SQLPWD;Trusted_Connection=True;MultipleActiveResultSets=true"
Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Tables OrderMasters ,OrderDetails
按 Enter 创建连接字符串、模型类和数据库上下文。
我们将在共享项目中看到 OrderMasters
和 OrderDetails Model
类以及 OrderManagementContext
类。我们将在服务器项目中使用此 Model
和 DBContext
来创建 Web API 以执行 CRUD 操作。
为获取订单明细创建 Web API
要创建我们的 WEB API 控制器,请右键单击“Controllers”文件夹。单击“添加新控制器”。
在这里,我们将使用 `Scaffold` 方法来创建我们的 WEB API。我们选择“带有操作的 API 控制器,使用 Entity Framework”。
从共享项目中选择我们的 `Model` 和 `DatabaseContext`。
从共享项目中选择我们的 OrderMasters
Model 以执行 CRUD 操作。
选择 Data Context Class 作为共享项目中的 OrderManagementContext
。如果您需要,将自动添加我们的 Controller
名称,您可以更改它然后点击 ADD。
我们将使用 Web API 中的所有 Get
/Post
/Put
和 Delete
方法。
与此相同,我们也为 OrderDetails
创建了一个 Web API。
要测试 Get
方法,我们可以运行我们的项目并复制 GET
方法 API 路径。在这里,我们可以看到获取 api/OrderMasters/ 的 API 路径。
运行程序并粘贴 API 路径以测试我们的输出。
对于 OrderDetails
,我们可以通过 Order No
来获取单个 Order
的明细。在这里,我们可以看到 Order No
为“1
”的结果,我们调用 Web API 为 /api/OrderDetails/1。
现在,我们将把所有这些 Web API JSON 结果绑定到我们 Client
项目的 Razor View 页面,以显示 Master
/Detail
HTML 表,用于执行 CRUD 操作,以及过滤和排序选项。
处理客户端项目
注意:在本文中,我们将创建 2 个 Razor 页面。在一个 Razor 页面中,我们将创建具有正常绑定方法的 Master Details
HTML 表,在另一个 Razor 页面中,我们将使用 Blazor 动态内容绑定 Detail
表。
具有正常绑定的 Razor 页面
首先,我们需要添加新的 Razor 视图页面。
添加 Razor 视图
要添加 Razor 视图页面,请右键单击 Client
项目中的 Pages 文件夹。点击 Add >> New Item
选择 Razor View >> 输入您的页面名称。在这里,我们将名称设为 Orders.cshtml。
在 Razor 视图页面中,我们有 3 部分代码:第一部分是 Import 部分,我们导入所有需要的引用和模型以在视图中使用;第二部分是 HTML 设计和数据绑定部分;最后,我们有函数部分,用于调用所有 Web API 以绑定到我们的 HTML 页面,并执行要在视图页面中显示的客户端业务逻辑。
导入部分
首先,我们在 Razor 视图页面中导入所有必需的支持文件和引用。在这里,我们首先导入了将在视图中使用的 Model
类,还导入了 HTTPClient
以调用 Web API 来执行 CRUD 操作。
@using MasterDetailCRUD.Shared
@using MasterDetailCRUD.Shared.Models
@page "/Orders"
@using Microsoft.AspNetCore.Blazor.Browser.Interop
@using System.Collections.Generic
@using Microsoft.AspNetCore.Blazor
Master HTML Grid 数据绑定部分
接下来,我们设计 Order
页面,将 Web API 中的 Order Master
和 Order Detail
结果绑定到 HTML 表。
绑定订单主明细
在 Init
方法中,我们从 Web API 获取 Order Master
并使用 foreach
循环将结果绑定到 HTML 表。在这里,我们为 Order Master
网格绑定结果,并在每行的第一个列中添加切换图像,在点击此切换图像时,我们调用 getOrderDetails(OrderNo)
方法以显示相关 Order
的 Detail
HTML 网格。我们将 Order No
传递给函数,我们获取相关 Order details
并将结果绑定到 detail grid
。此外,我们仅在点击切换图像按钮时显示 detail grid
,如果再次点击切换按钮,则隐藏 detail grid
。我们还提供了 Edit 和 Delete 按钮,用于从数据库中编辑和删除 Order Master
记录。
@foreach (var OrderMasterobj in ordMaster)
{
<tr style="border-style:dashed;border-width:2px;border-color:
@(OrderMasterobj.OrderNo == orderIDs ? "#ff6a00": "#a2aabe")">
<td align="center" style="border:
solid 1px #659EC7; padding: 5px;table-layout:fixed;">
@if (@OrderMasterobj.OrderNo == orderIDs)
{
<img src="@Imagename" onclick="@(async () =>
await getOrderDetails(@OrderMasterobj.OrderNo))" />
}
else
{
<img src="Images/toggle.png" onclick="@(async () =>
await getOrderDetails(@OrderMasterobj.OrderNo))" />
}
</td>
<td align="center" style="border: solid 1px #659EC7;
padding: 5px;table-layout:fixed;">
<span style="color:#9F000F">
<img src="Images/edit.gif" alt="Edit"
width="24px" height="24px" onclick="@(async () =>
await EditOrderMaster(@OrderMasterobj.OrderNo))" />
</span>
</td>
<td align="center" style="border: solid 1px #659EC7;
padding: 5px;table-layout:fixed;">
<span style="color:#9F000F">
<img src="Images/delete.gif" alt="Delete"
width="24px" height="24px" onclick="@(async () =>
await DeleteOrderMaster(@OrderMasterobj.OrderNo))" />
</span>
</td>
<td align="left" style="border: solid 1px #659EC7;
padding: 5px;table-layout:fixed;">
<span style="color:#9F000F">
@OrderMasterobj.OrderNo
</span>
</td>
<td align="left" style="border: solid 1px #659EC7;
padding: 5px;table-layout:fixed;">
<span style="color:#9F000F">
@OrderMasterobj.TableId
</span>
</td>
<td align="left" style="border: solid 1px #659EC7;
padding: 5px;table-layout:fixed;">
<span style="color:#9F000F">
@OrderMasterobj.Description
</span>
</td>
<td align="left" style="border: solid 1px #659EC7;
padding: 5px;table-layout:fixed;">
<span style="color:#9F000F">
@OrderMasterobj.OrderDate
</span>
</td>
<td align="left" style="border: solid 1px #659EC7;
padding: 5px;table-layout:fixed;">
<span style="color:#9F000F">
@OrderMasterobj.WaiterName
</span>
</td>
</tr>
函数部分
函数部分用于调用所有 Web API 以绑定到我们的 HTML 页面,并执行要在视图页面中显示的客户端业务逻辑。
Init 方法
在 Init
方法中,我们获取 Order Master
的 Web API 结果,并将其存储在 ordMaster
对象中,我们使用此对象通过 foreach
语句绑定到 HTML 表。
@functions {
OrderMasters[] ordMaster;
OrderDetails[] ordDetail;
OrderMasters ordsM = new OrderMasters();
OrderDetails ordsD = new OrderDetails();
Boolean showAddMaster = false;
Boolean showAddDetail = false;
int showDetailStatus = 0;
int sortStatus = 0;
int orderIDs = 0;
string Imagename = "Images/toggle.png";
string ImageSortname = "Images/sortAsc.png";
string Messages = "";
protected override async Task OnInitAsync()
{
ordMaster = await Http.GetJsonAsync<OrderMasters[]>("/api/OrderMasters/");
ordsD = new OrderDetails();
ordsM = new OrderMasters();
Messages = "";
}
在表头中添加排序功能
在这里,我们仅为主 HTML 表添加了排序和过滤功能。如果需要,我们也可以为 Detail grid 实现相同的功能。
在 Master Table 的表头部分,我们为每个表头添加了排序图像。在排序图像按钮的点击事件中,我们调用 Sorting
函数并将每个排序列名传递给点击事件。在函数代码部分,我们按升序和降序对 Web API 进行排序,并在每次点击列标题排序图像时显示结果。
<table style=" background-color:#FFFFFF; border: solid 2px #6D7B8D;
padding: 5px;width: 99%;table-layout:fixed;" cellpadding="2" cellspacing="2">
<tr style="height: 30px; background-color:#336699 ; color:#FFFFFF ;border: solid 1px #659EC7;">
<td width="120"></td>
<td width="40" align="center"><b>Edit</b></td>
<td width="40" align="center"><b>Delete</b></td>
<td width="120" align="center" style="cursor: pointer;">
<b>OrderNo</b>
<img src="@ImageSortname" onclick="@(async () =>
await OrderMasterSorting("OrderNo"))" height="24" width="24" />
</td>
<td width="240" align="center" style="cursor: pointer;">
<b>Table Name</b>
<img src="@ImageSortname" onclick="@(async () =>
await OrderMasterSorting("TableId"))" height="24" width="24" />
</td>
<td width="240" align="center" style="cursor: pointer;">
<b>Description</b>
<img src="@ImageSortname" onclick="@(async () =>
await OrderMasterSorting("Description"))" height="24" width="24" />
</td>
<td width="120" align="center" style="cursor: pointer;">
<b> Order Date</b>
<img src="@ImageSortname" onclick="@(async () =>
await OrderMasterSorting("OrderDate"))" height="24" width="24" />
</td>
<td width="340" align="center" style="cursor: pointer;">
<b> Waiter Name</b>
<img src="@ImageSortname" onclick="@(async () =>
await OrderMasterSorting("WaiterName"))" height="24" width="24" />
</td>
</tr>
排序方法
在排序图像点击每个列标题时,我们调用此方法并将列名传递给此方法。在此方法中,根据列名,我们对 Web API 结果进行排序并将结果绑定到 HTML 表。我们也进行升序和降序的切换排序。
//Order Master
protected async Task OrderMasterSorting(string SortColumn)
{
ordMaster = await Http.GetJsonAsync<OrderMasters[]>("/api/OrderMasters/");
Messages = "";
if (sortStatus == 1)
{
ImageSortname = "Images/sortDec.png";
sortStatus = 0;
switch (SortColumn)
{
case "OrderNo":
ordMaster = ordMaster.OrderBy(x => x.OrderNo).ToArray();
break;
case "TableId":
ordMaster = ordMaster.OrderBy(x => x.TableId).ToArray();
break;
case "Description":
ordMaster = ordMaster.OrderBy(x => x.Description).ToArray();
break;
case "OrderDate":
ordMaster = ordMaster.OrderBy(x => x.OrderDate).ToArray();
break;
case "WaiterName":
ordMaster = ordMaster.OrderBy(x => x.WaiterName).ToArray();
break;
}
}
else
{
ImageSortname = "Images/sortAsc.png";
sortStatus = 1;
switch (SortColumn)
{
case "OrderNo":
ordMaster = ordMaster.OrderByDescending(x => x.OrderNo).ToArray();
break;
case "TableId":
ordMaster = ordMaster.OrderByDescending(x => x.TableId).ToArray();
break;
case "Description":
ordMaster = ordMaster.OrderByDescending(x => x.Description).ToArray();
break;
case "OrderDate":
ordMaster = ordMaster.OrderByDescending(x => x.OrderDate).ToArray();
break;
case "WaiterName":
ordMaster = ordMaster.OrderByDescending(x => x.WaiterName).ToArray();
break;
}
}
}
在表头添加筛选功能
在 Table heading 部分,我们添加了一个新行。在 table row 中,我们为每个列添加了 Textbox
,用于对绑定结果执行过滤。在 Textbox onChange
事件中,我们调用代码函数部分的方法来执行过滤操作。
<tr style="height: 30px; background-color:#336699 ; color:#FFFFFF ;">
<td width="140" align="center" colspan="4">
<img src="Images/filter.png" width="24" height="24" /> Filter By
</td>
<td width="180" align="center" style="border:
solid 1px #FFFFFF; padding: 5px;table-layout:fixed;">
<input width="70"
onchange=@OnTableIdChanged oninput="
(this.dispatchEvent(new CustomEvent('change', {bubbles: true})))" />
</td>
<td width="180" align="center" style="border:
solid 1px #FFFFFF; padding: 5px;table-layout:fixed;">
<input width="70" onchange=@OnDescriptionChanged
oninput="(this.dispatchEvent(new CustomEvent('change', {bubbles: true})))" />
</td>
<td width="180" align="center"
style="border: solid 1px #FFFFFF; padding: 5px;table-layout:fixed;"></td>
<td width="180" align="center"
style="border: solid 1px #FFFFFF; padding: 5px;table-layout:fixed;">
<input width="70"
onchange=@OnWaiterNameChanged oninput="
(this.dispatchEvent(new CustomEvent('change', {bubbles: true})))" />
</td>
</tr>
筛选方法
在每个列标题部分,我们添加了一个新行来执行 HTML 网格的过滤。在每个列过滤器 Textbox 的 Change 事件中,我们传递 Textbox
值。我们调用一个公共过滤方法 OrderMasterFilteringList
,在该方法中,我们传递过滤列 Textbox
值和列名。
// For Filtering by OrderNo
void OnOrderNoChanged(UIChangeEventArgs args)
{
string values = args.Value.ToString();
OrderMasterFilteringList(values, "OrderNo");
}
// For Filtering by TableId
void OnTableIdChanged(UIChangeEventArgs args)
{
string values = args.Value.ToString();
OrderMasterFilteringList(values, "TableId");
}
// For Filtering by Description
void OnDescriptionChanged(UIChangeEventArgs args)
{
string values = args.Value.ToString();
OrderMasterFilteringList(values, "Description");
}
// For Filtering by OrderDate
void OnOrderDateChanged(UIChangeEventArgs args)
{
string values = args.Value.ToString();
OrderMasterFilteringList(values, "OrderDate");
}
// For Filtering by WaiterName
void OnWaiterNameChanged(UIChangeEventArgs args)
{
string values = args.Value.ToString();
OrderMasterFilteringList(values, "WaiterName");
}
在这里,我们创建了一个名为 OrderMasterFilteringList
的公共函数,在该方法中,我们获取过滤列 Textbox
值和列名。我们从 Web API 进行过滤并将过滤结果绑定到 HTML 表。
//Filtering
protected async Task OrderMasterFilteringList(String Value, string columnName)
{
ordMaster = await Http.GetJsonAsync<OrderMasters[]>("/api/OrderMasters/");
Messages = "";
if (Value.Trim().Length > 0)
{
switch (columnName)
{
case "TableId":
ordMaster = ordMaster.Where(x => x.TableId.Contains(Value)).ToArray();
break;
case "Description":
ordMaster = ordMaster.Where(x => x.Description.Contains(Value)).ToArray();
break;
case "WaiterName":
ordMaster = ordMaster.Where(x => x.WaiterName.Contains(Value)).ToArray();
break;
}
}
else
{
ordMaster = await Http.GetJsonAsync<OrderMasters[]>("/api/OrderMasters/");
}
}
插入新的订单主表
在 ADD New Order Master
按钮点击时,我们将使 New Order Master Add
表可见,用户可以在其中输入新的 Order
信息。
<table width="100%" style="background:#05163D;color:honeydew">
<tr>
<td width="20"> </td>
<td>
<h2> Add New Order Master Information</h2>
</td>
<td> </td>
<td align="right">
<button class="btn btn-info"
onclick="@AddNewOrderMasters">Add New Order</button>
</td>
<td width="10"> </td>
</tr>
<tr>
<td colspan="2"></td>
</tr>
</table>
对于新的 Order Master
,我们将 OrderNo
设置为 0
。在 New Order Master 保存按钮点击时,我们将调用保存方法。
当点击 Add New Order 按钮时,我们显示 Add/Edit Order Master,用户可以在此处更新或添加新的 Order Master。
@if (showAddMaster == true)
{
<table style=" background-color:#FFFFFF; border: dashed 3px #6D7B8D;
padding: 5px;width: 99%;table-layout:fixed;" cellpadding="2" cellspacing="2">
<tr style="height: 30px; background-color:#336699 ;
color:#FFFFFF ;border: solid 1px #659EC7;">
<td>
<h3> Add/Edit Order Master</h3>
</td>
</tr>
<tr>
<td>
<table class="form-group">
<tr>
<td>
<label for="Name"
class="control-label">Order No</label>
</td>
<td>
<input type="text"
class="form-control" bind="@ordsM.OrderNo" readonly />
</td>
<td width="20"> </td>
<td>
<label for="Name"
class="control-label">Table Name</label>
</td>
<td>
<input type="text"
class="form-control" bind="@ordsM.TableId" />
</td>
</tr>
<tr>
<td>
<label for="Email"
class="control-label">Description</label>
</td>
<td>
<input type="text" class="form-control"
bind="@ordsM.Description" />
</td>
<td width="20"> </td>
<td>
<label for="Name" class="control-label">Date</label>
</td>
<td>
<input type="text" class="form-control"
bind="@ordsM.OrderDate" />
</td>
</tr>
<tr>
<td>
<label for="Name" class="control-label">Waiter Name</label>
</td>
<td>
<input type="text" class="form-control"
bind="@ordsM.WaiterName" />
</td>
<td width="20"> </td>
<td></td>
<td>
<button type="submit" class="btn btn-success"
onclick="@(async () => await SaveOrderMasters())"
style="width:220px;">Save</button>
</td>
</tr>
</table>
</td>
</tr>
</table>
Save Method
在 Save 方法中,我们将检查 OrderNo
。如果 OrderNo
是“0
”,则将插入新的 Order Master
。在这里,我们将调用 Insert Web API 方法,如果 OrderNo
大于 0
,则表示更新 Order
记录。
//Save New or update Order Master
protected async Task SaveOrderDetails()
{
if (ordsD.OrderDetailNo == 0)
{
await Http.SendJsonAsync(HttpMethod.Post, "/api/OrderDetails/", ordsD);
}
else
{
await Http.SendJsonAsync(HttpMethod.Put, "/api/OrderDetails/" + ordsD.OrderDetailNo, ordsD);
}
ordDetail = await Http.GetJsonAsync<OrderDetails[]>
("/api/OrderDetails/" + Convert.ToInt32(ordsD.OrderNo));
ordsD = new OrderDetails();
showAddDetail = false;
showAddMaster = false;
Messages = "Order Detail Saved to Database !";
}
更新订单主表
与 Insert
相同,我们将显示更新详细信息供用户编辑 Order Master
并保存。在 Edit
方法中,我们将获取用户点击 Edit Icon 的行的所有详细信息,并将所有结果设置为相应的 TextBox
。在 Save 按钮点击时,我们将调用 save 方法将所有更改保存到数据库,类似于 Insert
。
//Edit Order Master
protected async Task EditOrderMaster(int OrderNos)
{
showAddMaster = true;
ordsM = await Http.GetJsonAsync<OrderMasters>("/api/OrderMasters/" +
Convert.ToInt32(OrderNos));
}
删除订单主详细信息
在 Delete 按钮点击时,我们将通过将 OrderNo
传递给 Web API 的 delete
方法来删除 Order
,从而从数据库中删除记录。
//Delete Order Master
protected async Task DeleteOrderMaster(int OrderNos)
{
await Http.DeleteAsync("/api/OrderMasters/" + Convert.ToInt32(OrderNos));
ordMaster = await Http.GetJsonAsync<OrderMasters[]>("/api/OrderMasters/");
Messages = "Order Master Deleted from Database !";
}
Detail Grid
绑定 Detail Grid 结果
首先,我们检查 OrderDetail
结果是否为 null
,如果不为 null
,则将结果绑定到 HTML 表。在绑定结果之前,我们还检查 showDetailStatus ==1
,这意味着显示或隐藏 Detail
Grid。默认情况下,我们将 showDetailStatus
设置为 0
,在 Master grid 切换图像点击事件中,我们将状态更改为 1
,如果 showDetailStatus
为 1
,则显示 Detail
grid,我们还检查 Master grid OrderNo
与 Order
detail 并将结果绑定到 Detail
Grid。我们将结果绑定到 Detail
Grid。
@if (ordDetail != null)
{
@if (showDetailStatus == 1)
{
@if (@OrderMasterobj.OrderNo == orderIDs)
{
<tr style="background-color:#6D7B8D ;
color:honeydew ;
border-style:dashed;border-width:2px;
border-color:#ECF3F4;">
<td colspan="8"
align="center">
Order Details of
Order NO - <strong> @OrderMasterobj.OrderNo
</strong> ,
Total @ordDetail.Length details for this Order
</td>
</tr>
<tr>
<td valign="top">
<button class="btn btn-info"
onclick="@(async () =>
await AddNewOrderDetails
(@OrderMasterobj.OrderNo))">Add New Detail</button>
</td>
<td colspan="7">
<table style="background-color:#ECF3F4; border: solid 2px #3273d5;
padding: 5px;width: 99%;table-layout:fixed;">
<tr style="height: 30px; background-color:#336699 ;
color:#FFFFFF ;">
<td width="40"
align="center"><b>Edit</b></td>
<td width="40"
align="center"><b>Delete</b></td>
<td width="240"
align="center">
<b> Order Number</b>
</td>
<td width="240" align="center">
<b> Order Detail Number</b>
</td>
<td width="120" align="center">
<b> Item Name</b>
</td>
<td width="340" align="center">
<b>Comments</b>
</td>
<td width="120" align="center">
<b> QTY</b>
</td>
<td width="120" align="center">
<b> Price</b>
</td>
</tr>
@foreach (var orderDetailsObj in ordDetail)
{
<tr>
<td align="center" style="border:
solid 1px #659EC7; padding: 5px;table-layout:fixed;">
<span style="color:#9F000F">
<img src="Images/edit.gif" alt="Edit"
width="24px" height="24px"
onclick="@(async () =>
await EditOrderDetails(@orderDetailsObj.OrderDetailNo))" />
</span>
</td>
<td align="center" style="border:
solid 1px #659EC7; padding: 5px;table-layout:fixed;">
<span style="color:#9F000F">
<img src="Images/delete.gif" alt="Delete"
width="24px" height="24px"
onclick="@(async () =>
await DeleteOrderDetails
(@orderDetailsObj.OrderDetailNo))" />
</span>
</td>
<td align="center" style="border:
solid 1px #659EC7; padding: 5px;table-layout:fixed;">
@orderDetailsObj.OrderNo
</td>
<td align="center" style="border:
solid 1px #659EC7; padding: 5px;table-layout:fixed;">
<span style="color:#9F000F">
@orderDetailsObj.OrderDetailNo
</span>
</td>
<td align="center" style="border:
solid 1px #659EC7; padding: 5px;table-layout:fixed;">
<span style="color:#9F000F">
@orderDetailsObj.ItemName
</span>
</td>
<td align="center" style="border:
solid 1px #659EC7; padding: 5px;table-layout:fixed;">
<span style="color:#9F000F">
@orderDetailsObj.Notes
</span>
</td>
<td align="right" style="border:
solid 1px #659EC7; padding: 5px;table-layout:fixed;">
<span style="color:#9F000F">
@orderDetailsObj.Qty
</span>
</td>
<td align="right" style="border:
solid 1px #659EC7; padding: 5px;table-layout:fixed;">
<span style="color:#9F000F">
@orderDetailsObj.Price
</span>
</td>
</tr>
}
</table>
</td>
</tr>
}
}
}
Detail Grid 绑定、隐藏/显示方法
在 Master grid 切换图像点击事件中,我们调用下面的方法,并将 Order NO
传递给它,以从 Web API 获取相应的 Order
详细信息,并将结果绑定到 detail HTML grid。
//--------------- Detail Grid CRUD
protected async Task getOrderDetails(int ordID)
{
showAddMaster = false;
showAddDetail = false;
Messages = "";
if (orderIDs != ordID)
{
Imagename = "Images/expand.png";
showDetailStatus = 1;
}
else
{
if (showDetailStatus == 0)
{
Imagename = "Images/expand.png";
showDetailStatus = 1;
}
else
{
Imagename = "Images/toggle.png";
showDetailStatus = 0;
}
}
orderIDs = ordID;
ordDetail = await Http.GetJsonAsync<OrderDetails[]>
("/api/OrderDetails/" + Convert.ToInt32(ordID));
}
订单明细添加/编辑和删除
与 Order Master
CRUD 相同,我们也为 Order Details
进行插入/编辑和删除。在这里,从下面的 gif 图像中,我们可以看到 Detail
grid 绑定、Insert
、Update
和 Delete
操作。
导航菜单
现在我们需要将这个新添加的 Orders Razor 页面添加到我们的左侧导航中。要添加它,请打开 Shared Folder 并打开 NavMenu.cshtml 页面并添加菜单。
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match=NavLinkMatch.All>
<span class="oi oi-home"
aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="Orders">
<span class="oi oi-plus"
aria-hidden="true"></span> Orders Master Detail
</NavLink>
</li>
构建并运行应用程序
关注点
在本文中,我们只添加了一层层级的网格,您可以将其扩展以创建多层级的 HTML 网格。注意,在创建 DBContext
和设置连接字符串时,不要忘记添加您的 SQL 连接字符串。希望大家喜欢这篇文章,在下一篇文章中,我们将看到更多关于 Blazor 的示例,与 Blazor 合作真的很酷也很棒。
历史
- 2018-07-09: MasterDetailCRUD.zip