65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (5投票s)

2018年10月28日

CPOL

9分钟阅读

viewsIcon

17673

downloadIcon

291

添加项目和项目状态处理

引言

这是多部分文章的第三部分,演示了通过 EntityFramework Core 2.1 (EF) 将 C# enum 值映射到数据库表中的 string 值。它解决了在与应用程序实体的一对多和多对多关系中映射 enum 值的问题。这在 ASP.NET Core Razor Page 应用程序的上下文中进行。

EF 是一个对象关系映射器 (ORM)。在像这个示例这样的应用程序中,存在两个“世界”。一个是作为 C# 中对象模型存在的对象世界。另一个是存在于关系数据库(如 Microsoft SQL Server)中的关系世界。这两个世界并不一致。ORM(如 EntityFramework)的功能是连接这两个世界,并促进它们之间的数据传输。

第一部分。设置 Entity Framework 数据上下文和初始 Customers Razor 页面

第二部分。完成 Customers 的 CRUD 功能

在第三部分。我们将创建 ProjectProjectState 实体,并实现 ProjectStateProject 之间的一对多关系,如下所示:

  • 添加 ProjectProjectStateProjectStateDescription 实体。
  • 添加 EF 迁移以在数据库中创建和配置 Projects 和 ProjectStateDescriptions 表。
  • 演示对象模型实体中的 enum 值与 Projects 和 ProjectStateDescriptions 数据库表中的 string 值之间的转换。
  • 生成、实现和测试 Project CRUD 页面、CustomerProjects.cshtmlCustomerProjectCreate.cshtmlCustomerProjectDetails.cshtmlCustomerProjectDelete.cshtml Razor 页面,其中包含 ProjectState 功能。

第四部分 添加 Skill 实体(Skill enumSkillTitleProjectSkill)并实现 ProjectsSkills 之间的多对多关系。

Using the Code

添加初始项目处理。

接下来,我们启用 Customer 项目处理。该应用程序使用 Customer 作为“网关”实体;所有内容都通过 Customer 访问。CustomerProjects 之间存在一对多关系。因此,我们需要修改 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,这些 ProjectProjects 列表表示。初始 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 的需求、初始预算和时间表。QuantumCustomer 均未做出承诺。
  • StartScheduled。已指定工作开始日期,并且正在进行开始工作的准备。
  • InProgress。实际工作已开始但尚未完成。
  • Completed。项目工作已完成。

如前所述,我们的应用程序有两个目标。

  1. 我们应该为每个 Project 状态定义一个简短的描述,该描述将在 UI 中显示,以帮助用户理解每个状态的含义。
  2. 每个 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

ProjectStateProjects 的一对多关系通过导航属性启用。每个 Project 都有一个 ProjectStateDesciption。每个 ProjectStateDescripton 都有一个 Projects 的集合。

接下来,我们需要定义 ProjectProjectStateDescription 的 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 表中配置一个名为 ProjectStateCodenvarchar(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

添加的行以粗体显示。现在为 ProjectProjectState 实体添加 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

修改用于 ProjectProjectState 的 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 页面在 .IncludeViewData 方面具有相同的特征。

初始 ~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 潜在或推荐的项目

摘要

我们已实现 ProjectStateProject 的一对多关系,并创建了 ASP.NET Razor 页面来管理它。

关注点

在本文的第四部分中,我们将添加 Skill 实体的定义(SkillSkillTitleProjectSkills),并实现 ProjectsSkills 之间的多对多关系。

© . All rights reserved.