ASP.NET Core Razor 页面使用 EntityFramework Core 和枚举作为字符串 - 第三部分





5.00/5 (5投票s)
添加项目和项目状态处理
引言
这是多部分文章的第三部分,演示了通过 EntityFramework Core 2.1 (EF) 将 C# enum
值映射到数据库表中的 string
值。它解决了在与应用程序实体的一对多和多对多关系中映射 enum
值的问题。这在 ASP.NET Core Razor Page 应用程序的上下文中进行。
EF 是一个对象关系映射器 (ORM)。在像这个示例这样的应用程序中,存在两个“世界”。一个是作为 C# 中对象模型存在的对象世界。另一个是存在于关系数据库(如 Microsoft SQL Server)中的关系世界。这两个世界并不一致。ORM(如 EntityFramework
)的功能是连接这两个世界,并促进它们之间的数据传输。
第一部分。设置 Entity Framework 数据上下文和初始 Customer
s Razor 页面
第二部分。完成 Customer
s 的 CRUD 功能
在第三部分。我们将创建 Project
和 ProjectState
实体,并实现 ProjectState
和 Project
之间的一对多关系,如下所示:
- 添加
Project
、ProjectState
和ProjectStateDescription
实体。 - 添加 EF 迁移以在数据库中创建和配置
Project
s 和ProjectStateDescription
s 表。 - 演示对象模型实体中的
enum
值与Project
s 和ProjectStateDescription
s 数据库表中的string
值之间的转换。 - 生成、实现和测试
Project
CRUD 页面、CustomerProjects.cshtml、CustomerProjectCreate.cshtml、CustomerProjectDetails.cshtml 和 CustomerProjectDelete.cshtml Razor 页面,其中包含ProjectState
功能。
第四部分。 添加 Skill 实体(Skill enum
、SkillTitle
和 ProjectSkill
)并实现 Projects
和 Skills
之间的多对多关系。
Using the Code
添加初始项目处理。
接下来,我们启用 Customer
项目处理。该应用程序使用 Customer
作为“网关”实体;所有内容都通过 Customer
访问。Customer
和 Projects
之间存在一对多关系。因此,我们需要修改 Customer
类。
修改后的 Customer.cs
using System.Collections.Generic;
namespace QuantumWeb.Model
{
/// <summary>
/// Customer Class
/// </summary>
public class Customer
{
#region Constructors
/// <summary>
/// Parameter-less Constructor
/// </summary>
/// <remarks>
/// Required for scaffolding the UI
/// </remarks>
public Customer()
{
} // end public Customer()
#endregion // Constructors
/// <summary>
/// Customer Identifier, primary key
/// </summary>
public int CustomerId { get; set; }
/// <summary>
/// Customer Name
/// </summary>
public string CustomerName { get; set; }
/// <summary>
/// Primary Customer Contact
/// </summary>
public string CustomerContact { get; set; }
/// <summary>
/// Customer Contact Phone Number
/// </summary>
public string CustomerPhone { get; set; }
/// <summary>
/// Customer Contact Email Address
/// </summary>
public string CustomerEmail { get; set; }
#region Navigation Properties
/// <summary>
/// List of Projects
/// </summary>
public List<Project> Projects { get; set; }
#endregion // Navigation Properties
} // end public class Customer
} // end namespace QuantumWeb.Model
我们添加了一个 Projects 列表(上面加粗显示)。在这里,我们将某些属性标识为导航属性。这些属性引用其他类/实体,以便我们可以在处理中导航到它们。一个 Customer
拥有零个或多个 Project
,这些 Project
由 Project
s 列表表示。初始 Project
类定义如下:
初始 Project.cs
namespace QuantumWeb.Model
{
/// <summary>
/// Project Class
/// </summary>
public class Project
{
/// <summary>
/// Project Identifier, primary key
/// </summary>
public int ProjectId { get; set; }
/// <summary>
/// Project Name
/// </summary>
public string ProjectName { get; set; }
#region Navigation Properties
/// <summary>
/// Customer Identifier
/// </summary>
public int CustomerId { get; set; }
/// <summary>
/// Customer
/// </summary>
/// <remarks>
/// Every Project has a Customer
/// </remarks>
public Customer Customer { get; set; }
/// <summary>
/// Project Status Code
/// </summary>
public ProjectState ProjectStateCode { get; set; }
/// <summary>
/// ProjectStateDescription Reference
/// </summary>
public ProjectStateDescription ProjectStateDescription { get; set; }
#endregion // Navigation Properties
} // end public class Project
} // end namespace QuantumApp.Model
除了定义初始 Project
类之外,我们还将在 Model 文件夹中定义 ProjectState enum
。
ProjectState.cs:
namespace QuantumWeb.Model
{
/// <summary>
/// Project State Enumeration
/// </summary>
public enum ProjectState
{
Prospect,
UnderReview,
StartScheduled,
InProgress,
Completed
} // end public enum ProjectState
} // end namespace QuantumWeb.Model
此 enum
指定了 Project
工作流的状态。
Prospect
。这表示一个潜在的Project
。此Project
可能通过推荐或其他营销活动获得。尚未进行研究,规格未知。UnderReview
。在此状态下,将开发Project
的需求、初始预算和时间表。Quantum
或Customer
均未做出承诺。StartScheduled
。已指定工作开始日期,并且正在进行开始工作的准备。InProgress
。实际工作已开始但尚未完成。Completed
。项目工作已完成。
如前所述,我们的应用程序有两个目标。
- 我们应该为每个
Project
状态定义一个简短的描述,该描述将在 UI 中显示,以帮助用户理解每个状态的含义。 - 每个
enum
值都将作为string
存储在数据库中。
为了满足 ProjectState enum
的这些要求,我们定义了 ProjectStateDescription
类。
ProjectStateDescription.cs:
using System.Collections.Generic;
namespace QuantumWeb.Model
{
/// <summary>
/// Project State Description Class
/// </summary>
public class ProjectStateDescription
{
/// <summary>
/// ProjectState Code
/// </summary>
public ProjectState ProjectStateCode { get; set; }
/// <summary>
/// State Description
/// </summary>
public string StateDescription { get; set; }
#region Navigation Properties
/// <summary>
/// Projects Collection
/// </summary>
public List<Project> Projects { get; set; }
#endregion // Navigation Properties
} // end public class ProjectStateDescription
} // end namespace QuantumWeb.Model
ProjectState
到 Projects
的一对多关系通过导航属性启用。每个 Project
都有一个 ProjectStateDesciption
。每个 ProjectStateDescripton
都有一个 Projects
的集合。
接下来,我们需要定义 Project
和 ProjectStateDescription
的 EF 配置类,并将所有内容包含在 QuantumDbContext
类中。所有这些活动都发生在 Data 文件夹中。
初始 ProjectConfiguration.cs
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using QuantumWeb.Model;
namespace QuantumWeb.Data
{
public class ProjectConfiguration : IEntityTypeConfiguration<Project>
{
public void Configure(EntityTypeBuilder<Project> builder)
{
builder.ToTable("Projects");
builder.HasKey(p => p.ProjectId);
builder.Property(p => p.ProjectId)
.HasColumnType("int");
builder.Property(p => p.ProjectName)
.IsRequired()
.HasColumnType("nvarchar(80)")
.HasMaxLength(80);
builder.Property(p => p.CustomerId)
.HasColumnType("int")
.IsRequired();
builder.HasOne(p => p.Customer)
.WithMany(c => c.Projects)
.HasForeignKey(p => p.CustomerId)
.IsRequired();
builder.Property(p => p.ProjectStateCode)
.HasColumnType("nvarchar(15)")
.HasDefaultValue(ProjectState.Prospect)
.HasConversion(
p => p.ToString(),
p => (ProjectState)Enum.Parse(typeof(ProjectState), p));
builder.HasOne(p => p.ProjectStateDescription)
.WithMany(pd => pd.Projects)
.HasForeignKey(p => p.ProjectStateCode);
} // end public void Configure(EntityTypeBuilder<Project> builder)
} // end public class ProjectConfiguration : IEntityTypeConfiguration<Project>
} // end namespace QuantumWeb.Data
查看下面的提取行
builder.HasOne(p => p.Customer)
.WithMany(c => c.Projects)
.HasForeignKey(p => p.CustomerId)
.IsRequired();
对这些行的解释是:“每个 Project
都有一个 Customer
和多个 Projects
。每个 Project
映射到数据库中的 Projects
表,具有外键 CustomerId
,并且是必需的。因此,Customer
-Project
关系是一对多的。”
ProjectStateDescription
-Project
的一对多关系通过以下方式配置:
builder.HasOne(p => p.ProjectStateDescription)
.WithMany(pd => pd.Projects)
.HasForeignKey(p => p.ProjectStateCode);
接下来,我们看一下如何处理 enum
值到数据库 string
列的配置。
builder.Property(p => p.ProjectStateCode)
.HasColumnType("nvarchar(15)")
.HasDefaultValue(ProjectState.Prospect)
.HasConversion(
p => p.ToString(),
p => (ProjectState)Enum.Parse(typeof(ProjectState), p));
这些行首先在 Projects
表中配置一个名为 ProjectStateCode
的 nvarchar(15)
列,其默认值取自 ProjectState.Prospect
。接下来,定义了 ProjectState
值与 string
值之间的转换。当将值从 ProjectState enum
移到 Projects
表时,使用 ToString()
函数转换值。反向转换时,将表中的 string
值解析为 enum
值。整个过程中使用相同的方案将 enum
值和数据库列中的 string
值进行转换。
下面显示了 ProjectStateDescriptionConfiguration
类。
ProjectStateDescriptionConfiguration.cs:
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using QuantumWeb.Model;
namespace QuantumWeb.Data
{
/// <summary>
/// ProjectState Description Configuration Class
/// </summary>
public class ProjectStateDescriptionConfiguration :
IEntityTypeConfiguration<ProjectStateDescription>
{
public void Configure(EntityTypeBuilder<ProjectStateDescription> builder)
{
builder.ToTable("ProjectStateDescriptions");
builder.HasKey(p => p.ProjectStateCode);
builder.Property(p => p.ProjectStateCode)
.HasColumnType("nvarchar(15)")
.HasConversion(
p => p.ToString(),
p => (ProjectState)Enum.Parse(typeof(ProjectState), p));
builder.Property(p => p.StateDescription)
.IsRequired()
.HasColumnType("nvarchar(80)")
.HasMaxLength(80);
} // end public void Configure(EntityTypeBuilder<ProjectStateDescription> builder)
} // end public class ProjectStateDescriptionConfiguration :
// IEntityTypeConfiguration<ProjectStateDescription>
} // end namespace QuantumWeb.Data
现在,我们更新 QuantumDbContext
类。
第二次更新 QuantumDbContext.cs
using Microsoft.EntityFrameworkCore;
using QuantumWeb.Model;
namespace QuantumWeb.Data
{
public class QuantumDbContext : DbContext
{
public QuantumDbContext (DbContextOptions<QuantumDbContext> options)
: base(options)
{
} // end public QuantumDbContext (DbContextOptions<QuantumDbContext> options)
#region DbSets
/// <summary>
/// Customer DbSet
/// </summary>
public DbSet<Customer> Customers { get; set; }
/// <summary>
/// Project DbSet
/// </summary>
public DbSet<Project> Projects { get; set; }
/// <summary>
/// ProjectStateDescription DbSet
/// </summary>
public DbSet<ProjectStateDescription> ProjectStateDescriptions { get; set; }
#endregion // DbSets
/// <summary>
/// Data Model Creation Method
/// </summary>
/// <param name="modelBuilder">ModelBuilder instance</param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new CustomerConfiguration());
modelBuilder.ApplyConfiguration(new ProjectConfiguration());
modelBuilder.ApplyConfiguration(new ProjectStateDescriptionConfiguration());
} // end protected override void OnModelCreating(ModelBuilder modelBuilder)
} // end public class QuantumDbContext : DbContext
} // end namespace QuantumWeb.Data
添加的行以粗体显示。现在为 Project
和 ProjectState
实体添加 EF 迁移。
Add-Migration Added-Project-ProjectState
生成的 ~\Migrations\20181021203503_Added-Project-ProjectState.cs
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
namespace QuantumWeb.Migrations
{
public partial class AddedProjectProjectState : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ProjectStateDescriptions",
columns: table => new
{
ProjectStateCode =
table.Column<string>(type: "nvarchar(15)", nullable: false),
StateDescription =
table.Column<string>(type: "nvarchar(80)", maxLength: 80, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ProjectStateDescriptions", x => x.ProjectStateCode);
});
migrationBuilder.CreateTable(
name: "Projects",
columns: table => new
{
ProjectId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy",
SqlServerValueGenerationStrategy.IdentityColumn),
ProjectName = table.Column<string>(type: "nvarchar(80)",
maxLength: 80, nullable: false),
CustomerId = table.Column<int>(type: "int", nullable: false),
ProjectStateCode = table.Column<string>
(type: "nvarchar(15)", nullable: false, defaultValue: "Prospect")
},
constraints: table =>
{
table.PrimaryKey("PK_Projects", x => x.ProjectId);
table.ForeignKey(
name: "FK_Projects_Customers_CustomerId",
column: x => x.CustomerId,
principalTable: "Customers",
principalColumn: "CustomerId",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Projects_ProjectStateDescriptions_ProjectStateCode",
column: x => x.ProjectStateCode,
principalTable: "ProjectStateDescriptions",
principalColumn: "ProjectStateCode",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Projects_CustomerId",
table: "Projects",
column: "CustomerId");
migrationBuilder.CreateIndex(
name: "IX_Projects_ProjectStateCode",
table: "Projects",
column: "ProjectStateCode");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Projects");
migrationBuilder.DropTable(
name: "ProjectStateDescriptions");
}
}
}
在 Update
-Database
命令之后,下面显示了来自 SQL Server Management Studio (SSMS) 的数据库图。
QuantumDbContext
数据库图,包含 Customer
-Project
-ProjectState
表
修改用于 Project
和 ProjectState
的 Razor 页面。
我们需要为 Projects 向应用程序添加一些自定义 Customer Razor 页面。首先,我们需要向 Customer/Index 页面添加一个指向 CustomerProjects
的链接。
将 CustomerProjects
链接添加到 Pages\Customers\Index.cshtml
@page
@model QuantumWeb.Pages.Customers.IndexModel
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-page="Create">Create New</a>
<!-- A link to the Pages/Customers/Create page to create a new Customer -->
</p>
<!-- An HTML table to display existing Customers -->
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Customer[0].CustomerName)
</th>
<th>
@Html.DisplayNameFor(model => model.Customer[0].CustomerContact)
</th>
<th>
@Html.DisplayNameFor(model => model.Customer[0].CustomerPhone)
</th>
<th>
@Html.DisplayNameFor(model => model.Customer[0].CustomerEmail)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Customer) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.CustomerName)
</td>
<td>
@Html.DisplayFor(modelItem => item.CustomerContact)
</td>
<td>
@Html.DisplayFor(modelItem => item.CustomerPhone)
</td>
<td>
@Html.DisplayFor(modelItem => item.CustomerEmail)
</td>
<td>
<a asp-page="./Edit" asp-route-id="@item.CustomerId">Edit</a> |
<!-- A link to the Pages/Customers/Edit page to edit an existing Customer -->
<a asp-page="./Details" asp-route-id="@item.CustomerId">Details</a> |
<!--
A link to the Pages/Customers/Details page to display the details for an existing
Customer
-->
<a asp-page="./CustomerProjects" asp-route-id="@item.CustomerId">Projects</a> |
<!--
A link to the Pages/Customers/CustomerProjects page to display & manage the
Projects for an existing Customer
-->
<a asp-page="./Delete" asp-route-id="@item.CustomerId">Delete</a>
<!-- A link to the Pages/Customers/Delete page to delete an existing Customer -->
</td>
</tr>
}
</tbody>
</table>
我们将按以下方式生成几个自定义 Customers
Razor 页面。
Customers Razor 页面的自定义生成
生成 Customers/CustomerProjects Razor 页面
点击“Add”将为 CustomerProjects
Index 页面生成 Shell 文件。
生成的 ~Pages\Customers\CustomerProjects.cshtml
@page
@model QuantumWeb.Pages.Customers.CustomerProjectsModel
@{
ViewData["Title"] = "CustomerProjects";
}
<h2>CustomerProjects</h2>
生成的 ~Pages\Customers\CustomerProjects.cshtml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace QuantumWeb.Pages.Customers
{
public class CustomerProjectsModel : PageModel
{
public void OnGet()
{
}
}
}
我们将根据需要修改这些 Shell 文件。修改后的 CustomerProjects
Index 页面的文件如下:
修改后的 ~Pages\Customers\CustomerProjects.cshtml
@page "{id:int?}"
@model QuantumWeb.Pages.Customers.CustomerProjectsModel
@{
ViewData["Title"] = "Customer Projects";
}
<h2>Customer Projects</h2>
<div>
<h4>Customer</h4>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Customer.CustomerId)
</dt>
<dd>
@Html.DisplayFor(model => model.Customer.CustomerId)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Customer.CustomerName)
</dt>
<dd>
@Html.DisplayFor(model => model.Customer.CustomerName)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Customer.Projects)
</dt>
<dd>
<table class="table">
<tr>
<th>Project ID</th>
<th>Project Name</th>
<th>Project State</th>
<th></th>
</tr>
@foreach (var item in Model.Customer.Projects)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.ProjectId)
</td>
<td>
@Html.DisplayFor(modelItem => item.ProjectName)
</td>
<td>
@Html.DisplayFor(modelItem => item.ProjectStateCode)
</td>
<td>
<a asp-page="./CustomerProjectEdit"
asp-route-id="@item.ProjectId">Edit</a> |
<a asp-page="./CustomerProjectDelete"
asp-route-id="@item.ProjectId">Delete</a>
</td>
</tr>
}
</table>
</dd>
</dl>
</div>
<div>
<a asp-page="CustomerProjectCreate" asp-route-id="@Model.Customer.CustomerId">
Create New Project</a> |
<a asp-page="./Index">Back to List</a>
</div>
“{id:int?}
”表示需要一个整数参数 id
,否则对该页面的请求将返回 HTTP 401(页面未找到)错误。在这种情况下,这是目标 Customer
的标识符(CustomerId
)。另外,请注意引用 CustomerProjectCreate
页面的链接。
<a asp-page="CustomerProjectCreate" asp-route-id="@Model.Customer.CustomerId">创建新项目</a> |
这将带我们到尚未创建的 CustomerProjectCreate
页面,以创建引用的 Customer
的新 Project
。
修改后的 ~Pages\Customers\CustomerProjects.cshtml.cs
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using QuantumWeb.Data;
using QuantumWeb.Model;
namespace QuantumWeb.Pages.Customers
{
public class CustomerProjectsModel : PageModel
{
private readonly QuantumDbContext _context;
public CustomerProjectsModel(QuantumDbContext context)
{
_context = context;
} // end public CustomerProjectsModel(QuantumDbContext context)
public Customer Customer { get; set; }
public async Task<IActionResult> OnGet(int? id)
{
if (id == null)
{
return NotFound();
} // endif (id == null)
Customer = await _context.Customers
.Include(c => c.Projects)
.FirstOrDefaultAsync(c => c.CustomerId == id);
if (Customer == null)
{
return NotFound();
} // endif (Customer == null)
return Page();
} // end public async Task<IActionResult> OnGet(int? id)
} // end public class CustomerProjectsModel : PageModel
} // end namespace QuantumWeb.Pages.Customers
请注意,这里的 OnGet
处理程序有一个可空的整数参数 id
,这应该是上面提到的 CustomerId
。
QuantumWeb
应用程序 Customers 页面:https//: 44306/Customers,带有 Project 链接。
Customer Projects
页面:https//: 44306/Customers/CustomerProjects/1 (无 Projects)
“Create New Project”链接将激活自定义 CustomerProjectCreate
Razor 页面。我们现在生成此页面。
生成 Customers/CustomerProjectCreate Razor 页面
初始 ~Pages\Customers\CustomerProjectCreate.cshtml.cs
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using QuantumWeb.Data;
using QuantumWeb.Model;
namespace QuantumWeb.Pages.Customers
{
public class CustomerProjectCreateModel : PageModel
{
private readonly QuantumDbContext _context;
public CustomerProjectCreateModel(QuantumDbContext context)
{
_context = context;
} // end public CustomerProjectCreateModel(QuantumContext context)
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnGet(int? id)
{
if (id == null)
{
return NotFound();
} // endif (id == null)
Customer = await _context.Customers
.Include(c => c.Projects)
.FirstOrDefaultAsync(c => c.CustomerId == id);
if (Customer == null)
{
return NotFound();
} // endif (Customer == null)
ViewData["ProjectStateCode"] = new SelectList(_context.ProjectStateDescriptions,
"ProjectStateCode", "StateDescription", ProjectState.Prospect);
return Page();
} // end public async Task<IActionResult> OnGet(int? id)
[BindProperty]
public Project Project { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
} // endif (!ModelState.IsValid)
Project.CustomerId = Customer.CustomerId;
_context.Projects.Add(Project);
await _context.SaveChangesAsync();
return RedirectToPage("./CustomerProjects", new { id = Customer.CustomerId });
} // end public async Task<IActionResult> OnPostAsync()
} // end public class CustomerProjectCreateModel : PageModel
} // end namespace QuantumWeb.Pages.Customers
请注意此代码中的这些行。
[BindProperty]
public Customer Customer { get; set; }
[BindProperty]
将 Customer
实例绑定到 UI 元素,以便在浏览器和 Web 服务器之间保留其值。另外,请注意此属性也应用于 Project
实例。
Customer = await _context.Customers
.Include(c => c.Projects)
.FirstOrDefaultAsync(c => c.CustomerId == id);
此语句针对数据库执行查询,以检索主键值 CustomerId
与输入参数 id
值匹配的 Customer
记录及其相关的 Project
记录(如果有)。.Include
的作用是将关联记录包含在查询中。
ViewData["ProjectStateCode"] = new SelectList(_context.ProjectStateDescriptions,
"ProjectStateCode", "StateDescription", ProjectState.Prospect);
ViewData
是一个未类型化的键值字典,用于在 CustomerProjectCreateModel
类(在 .cshtml.cs 文件中)和 .cshtml 文件中的 HTML 之间传递值。这类似于在 MVC 中将数据从 Controller
传递到 View。使用 ViewData
时,数据仅在 HTTP 请求中持久。其成员从 ProjectStateDescriptions
数据库表的查询中填充。在这种情况下,_context.ProjectStateDescriptions
是查询返回的 IEnumerable<ProjectStateDescription>
。ProjectStateCode
是表中的主键,代表 ViewData
字典中的键。StateDescription
成为 ViewData.
字典中的关联值。ViewData
将用于填充 CustomerProjectCreate.cshtml 中的 <select>
元素。(见下文。)ProjectState.Prospect
是 <select>
的默认选定值,取自 ProjectState enum
。您可以在链接 https://www.tektutorialshub.com/viewbag-viewdata-asp-net-core/ 上阅读更多关于 ViewData
的信息。
初始 ~Pages\Customers\CustomerProjectCreate.cshtml
@page
@model QuantumWeb.Pages.Customers.CustomerProjectCreateModel
@{
ViewData["Title"] = "Create Customer Project";
}
<h2>Create Customer Project</h2>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Customer.CustomerId)
</dt>
<dd>
@Html.DisplayFor(model => model.Customer.CustomerId)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Customer.CustomerName)
</dt>
<dd>
@Html.DisplayFor(model => model.Customer.CustomerName)
</dd>
</dl>
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Customer.CustomerId" />
<div class="form-group">
<label asp-for="Project.ProjectName" class="control-label"></label>
<input asp-for="Project.ProjectName" class="form-control">
</div>
<div class="form-group">
<label asp-for="Project.ProjectStateCode" class="control-label"></label>
<select asp-for="Project.ProjectStateCode" class="form-control"
asp-items="ViewBag.ProjectStateCode">
</select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="CustomerProjects" asp-route-id="@Model.Customer.CustomerId">
Back to Customer Projects
</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
关键要素如下:
<input type="hidden" asp-for="Customer.CustomerId" />
<input>
捕获目标 CustomerId
,以便在 <form>
发布到创建 Project
时可用。 <select asp-for="Project.ProjectStateCode" class="form-control"
asp-items="ViewBag.ProjectStateCode">
</select>
此 <select>
元素将在 UI 中显示为一个下拉列表,其中包含在 CustomerProjectCreate.OnGet()
方法中填充的 ViewData
中的值。
初始 ~Pages\Customers\CustomerProjectCreate.cshtml
这显示了最初显示的 Customers/CustomerProjectCreate 页面。
带有数据的 CustomerProjectCreate
页面
点击“Create
”后,我们将看到:
客户 Projects
页面,已添加 Project
接下来的两张图显示了为两个 Customers
添加其他 Projects
之后的情况。
Mirarex Oil & Gas 的客户 Projects 页面(2 个 Projects)
Polyolefin Processing, Inc. 的客户 Projects 页面(3 个 Projects)
现在,我们可以添加另一个页面来编辑 Customer
Projects,即 CustomerProjectEdit
页面。
生成 Customers/CustomerProjectEdit Razor 页面
初始 ~Pages\Customers\CustomerProjectEdit.cshtml.cs
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using QuantumWeb.Data;
using QuantumWeb.Model;
namespace QuantumApp.Pages.Customers
{
public class CustomerProjectEditModel : PageModel
{
private readonly QuantumDbContext _context;
public CustomerProjectEditModel(QuantumDbContext context)
{
_context = context;
} // end public CustomerProjectEditModel(QuantumDbContext context)
[BindProperty]
public Customer Customer { get; set; }
[BindProperty]
public Project Project { get; set; }
public async Task<IActionResult> OnGet(int? id)
{
if (id == null)
{
return NotFound();
} // endif (id == null)
Project = await _context.Projects
.Include(p => p.Customer)
.FirstOrDefaultAsync(p => p.ProjectId == id);
if (Project == null)
{
return NotFound();
} // endif (Project == null)
Customer = Project.Customer;
ViewData["ProjectStateCode"] = new SelectList(_context.ProjectStateDescriptions,
"ProjectStateCode", "StateDescription", ProjectState.Prospect);
return Page();
} // end public async Task<IActionResult> OnGet(int? id)
public async Task<IActionResult> OnPostAsync(int? id)
{
if (!ModelState.IsValid)
{
return Page();
} // endif (!ModelState.IsValid)
var projectToUpdate = await _context.Projects.FindAsync(id);
if (projectToUpdate == null)
{
return NotFound();
} // endif (projectToUpdate == null)
projectToUpdate.CustomerId = Customer.CustomerId;
if (await TryUpdateModelAsync<Project>(
projectToUpdate,
"project",
p => p.ProjectName, p => p.ProjectStateCode))
{
await _context.SaveChangesAsync();
return RedirectToPage("./CustomerProjects", new { id = Customer.CustomerId });
}
return Page();
} // end public async Task<IActionResult> OnPostAsync(int? id)
} // end public class CustomerProjectEditModel : PageModel
} // end namespace QuantumApp.Pages.Customers
此代码与 CustomerProjectCreate
页面在 .Include
和 ViewData
方面具有相同的特征。
初始 ~Pages\Customers\CustomerProjectEdit.cshtml
@page "{id:int?}"
@model QuantumWeb.Pages.Customers.CustomerProjectEditModel
@{
ViewData["Title"] = "Edit Customer Project";
}
<h2>Edit Customer Project</h2>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Customer.CustomerId)
</dt>
<dd>
@Html.DisplayFor(model => model.Customer.CustomerId)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Customer.CustomerName)
</dt>
<dd>
@Html.DisplayFor(model => model.Customer.CustomerName)
</dd>
</dl>
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Customer.CustomerId" />
<div class="form-group">
<label asp-for="Project.ProjectName" class="control-label"></label>
<input asp-for="Project.ProjectName" class="form-control">
</div>
<div class="form-group">
<label asp-for="Project.ProjectStateCode" class="control-label"></label>
<select asp-for="Project.ProjectStateCode" class="form-control"
asp-items="ViewBag.ProjectStateCode">
</select>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="CustomerProjects" asp-route-id="@Model.Customer.CustomerId">
Back to Customer Projects
</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
此页面与 CustomerProjectCreate
页面在用于 CustomerId
的隐藏 <input>
和 <select>
方面具有相同的元素。
Mirarex Oil & Gas 的客户 Projects 页面(2 个 Projects)- 用于编辑
Mirarex Oil & Gas 的客户 Project 编辑页面,Zolar Pipeline
Mirarex Oil & Gas 的客户 Projects 页面(2 个 Projects)- 项目已编辑
本部分最后一个功能是通过 CustomerProjectDelete
页面删除项目。
生成 Customers/CustomerProjectDelete Razor 页面
初始 ~Pages\Customers\CustomerProjectDelete.cshtml.cs
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using QuantumWeb.Data;
using QuantumWeb.Model;
namespace QuantumWeb.Pages.Customers
{
public class CustomerProjectDeleteModel : PageModel
{
private readonly QuantumDbContext _context;
public CustomerProjectDeleteModel(QuantumDbContext context)
{
_context = context;
} // end public CustomerProjectDeleteModel(QuantumContext context)
[BindProperty]
public Customer Customer { get; set; }
[BindProperty]
public Project Project { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
} // endif (id == null)
Project = await _context.Projects
.Include(p => p.Customer)
.FirstOrDefaultAsync(p => p.ProjectId == id);
if (Project == null)
{
return NotFound();
} // endif (Project == null)
Customer = Project.Customer;
return Page();
} // end public async Task<IActionResult> OnGet(int? id)
public async Task<IActionResult> OnPostAsync(int? id)
{
if (id == null)
{
return NotFound();
} // endif (id == null)
Project = await _context.Projects
.Include(p => p.Customer)
.FirstOrDefaultAsync(p => p.ProjectId == id);
if (Project != null)
{
_context.Projects.Remove(Project);
await _context.SaveChangesAsync();
} // endif (Project != null)
return RedirectToPage("./CustomerProjects", new { id = Project.Customer.CustomerId });
} // end public async Task<IActionResult> OnPostAsync(int? id)
} // end public class CustomerProjectDeleteModel : PageModel
} // end namespace QuantumWeb.Pages.Customer
初始 ~Pages\Customers\CustomerProjectDelete.cshtml
@page "{id:int?}"
@model QuantumWeb.Pages.Customers.CustomerProjectDeleteModel
@{
ViewData["Title"] = "Delete Customer Project";
}
<h2>Delete Customer Project</h2>
<h3>Are you sure you want to delete this?</h3>
<div>
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Customer.CustomerName)
</dt>
<dd>
@Html.DisplayFor(model => model.Customer.CustomerName)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Project.ProjectId)
</dt>
<dd>
@Html.DisplayFor(model => model.Project.ProjectId)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Project.ProjectName)
</dt>
<dd>
@Html.DisplayFor(model => model.Project.ProjectName)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Project.ProjectStateCode)
</dt>
<dd>
@Html.DisplayFor(model => model.Project.ProjectStateCode)
</dd>
</dl>
<form method="post">
<input type="hidden" asp-for="Project.ProjectId" />
<a asp-page="CustomerProjects" asp-route-id="@Model.Customer.CustomerId">
Back to Customer Projects
</a> |
<input type="submit" value="Delete" class="btn btn-default" />
</form>
</div>
Mirarex Oil & Gas 的客户 Projects 页面(3 个 Projects)
删除客户 Projects 页面 - 删除 Ouachita Shale
Mirarex Oil & Gas 的客户 Projects 页面(2 个 Projects)
此时,我们可以将测试数据总结为下表:
Customers, Projects, ProjectStates
CustomerId | 客户名称 | ProjectId | 项目名称 | ProjectStateCode | StateDescription |
1 | Mirarex Oil & Gas, LLC | 1 | Zolar Pipeline | UnderReview | 项目正在审核和谈判中 |
1 | Mirarex Oil & Gas, LLC | 2 | Nelar Ranch Gas Fracturing | Prospect | 潜在或推荐的项目 |
2 | Polyolefin Processing, Inc. | 3 | Port Gibson Plant Expansion | Prospect | 潜在或推荐的项目 |
2 | Polyolefin Processing, Inc. | 4 | Jackson Plant Control System Upgrade | Prospect | 潜在或推荐的项目 |
2 | Polyolefin Processing, Inc. | 5 | Eutaw Plant Shutdown & Maintenance | Prospect | 潜在或推荐的项目 |
摘要
我们已实现 ProjectState
到 Project
的一对多关系,并创建了 ASP.NET Razor 页面来管理它。
关注点
在本文的第四部分中,我们将添加 Skill
实体的定义(Skill
、SkillTitle
和 ProjectSkills
),并实现 Projects
和 Skills
之间的多对多关系。