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

ASP.NET Core Razor Pages 使用 EntityFramework Core 和枚举字符串 - 第四部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (7投票s)

2018年10月28日

CPOL

7分钟阅读

viewsIcon

15975

downloadIcon

223

添加项目和项目技能处理

引言

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

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

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

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

第三部分。 创建 Project 和 ProjectState 实体,并在 ProjectState 和 Projects 之间实现一对多关系

在第四部分:添加 Skill 实体(Skill enumSkillTitleProjectSkill)并实现 Projects 和 Skills 之间的多对多关系,如下所示:

  • 添加实体 SkillSkillTitleProjectSkill,并配置到 QuantumDbContext 中。
  • 添加迁移、Add-Project-Skill-Entities,并更新数据库。
  • 为 Skill 相关 Razor 页面生成脚手架,CustomerProjectSkills.cshtmlCustomerProjectSkillAssign.cshtmlCustomerProjectSkillDelete.cshtml。运行测试以确认功能。

背景

本系列中实现的示例应用程序是为虚构的工程技术公司 Quantum Engineering Technologies 制作的。Quantum 主要服务于石油、天然气和化学工业。到目前为止,我们已经讨论了对象模型的基础,并创建了许多 ASP.NET Razor Pages 来处理 CustomersProjectsProject 状态。在最后一部分,我们将讨论执行工作所需的技能列表。这项工作围绕 Skill enum 以及 Skills 和 Projects 之间的多对多关系展开。一项 Skill 可能需要多个项目。一个项目也可能需要多种 Skill。因此,Project 和 Skill 实体之间存在多对多关系。

在关系数据库中,多对多关系通过为这两个实体创建一个 join 表来实现,如下所示:

通过 Join 表实现的多对多关系

TABLE A 和 TABLE B 之间存在多对多关系。在关系数据库中,可以创建一个 JOIN-TABLE A-B,它与两个表都存在一对多关系。

在 Entity Framework (EF) 的早期版本中,join 表可以通过约定进行配置,而无需在对象模型中创建实体或类。然而,截至目前,EntityFramework Core 尚未提供此功能。(请参阅此链接。)因此,我们需要创建一个类并在我们的对象模型中进行配置来实现此目的。接下来的讨论将展示如何做到这一点,以及相关的 Razor Pages 来在 UI 中管理这些内容。

Using the Code

工作对象模型在第一部分中展示,此处为 Skills 重复。

Skills 工作对象模型

Skill.cs

namespace QuantumWeb.Model
{
    /// <summary>
    /// Skill Enumeration
    /// </summary>
    public enum Skill
    {
        Programmer, // Programmer
        ME,         // Mechanical Engineer
        ChE,        // Chemical Engineer
        CtrlE,      // Control Engineer
        SWE,        // Software Engineer
        SWArch,     // Software Architect
        WebDes,     // Web Designer
        DataSci,    // Data Scientist
        ProjMgr,    // Project Manager
        BusAnal,    // Business Analyst
        QA          // Quality Assurance Tester
    } // end public enum Skill

} // end namespace QuantumWeb.Model

我们提供了一个 SkillTitle 类,以便在 UI 中为 enum 值提供相关标题。

SkillTitle.cs

using System.Collections.Generic;

namespace QuantumWeb.Model
{
    /// <summary>
    /// SkillTitle Class
    /// </summary>
    public class SkillTitle
    {
        /// <summary>
        /// Skill Code
        /// </summary>
        public Skill SkillCode { get; set; }
        /// <summary>
        /// Skill Title
        /// </summary>
        public string Title { get; set; }

        #region Navigation Properties

        /// <summary>
        /// List of ProjectSkill Instances
        /// </summary>
        public List<ProjectSkill> ProjectSkills { get; set; }

        #endregion // Navigation Properties

    } // end public class SkillTitle

} // end namespace QuantumWeb.Model

此类引用 ProjectSkill 类,该类将被配置为数据库中的 join 表。

ProjectSkill.cs

namespace QuantumWeb.Model
{
    /// <summary>
    /// ProjectSkill Class, a join entity
    /// </summary>
    /// <remarks>
    /// Note: The database table will have a non-unique index on ProjectId and SkillCode
    /// </remarks>
    public class ProjectSkill
    {

        /// <summary>
        /// ProjectSkill Identifier, primary key
        /// </summary>
        public int ProjectSkillId { get; set; }

        #region Navigation Properties

        /// <summary>
        /// Project Identifier
        /// </summary>
        public int ProjectId { get; set; }
        /// <summary>
        /// Project Reference
        /// </summary>
        public Project Project { get; set; }
        /// <summary>
        /// Skill Code
        /// </summary>
        public Skill SkillCode { get; set; }
        /// <summary>
        /// SkillTitle Reference
        /// </summary>
        public SkillTitle SkillTitle { get; set; }

        #endregion // Navigation Properties

    } // end public class ProjectSkill

} // end namespace QuantumWeb.Model

请注意导航属性。这里有一个指向单个 Project 实例的引用和一个指向单个 SkillTitle 实例的引用。ProjectIdSkillCode 属性将映射到数据库中 ProjectSkills 表的外键。另外,请注意 SkillTitle 类有一个集合属性 ProjectSkills(一个 List<ProjectSkill>),这表明它可以与零个或多个 ProjectSkill 实例相关联。我们现在必须修改 Project 类。

修改后的 Project.cs

using System.Collections.Generic;

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; }

        /// <summary>
        /// List of ProjectSkill Instances
        /// </summary>
        public List<ProjectSkill> ProjectSkills { get; set; }

        #endregion // Navigation Properties

    } // end public class Project

} // end namespace QuantumApp.Model

在这里,我们添加了一个导航属性 ProjectSkills(一个 List<ProjectSkill>),它允许一个 Project 与多个 ProjectSkill 实例相关联。

现在我们必须在 QuantumDbContext 类中配置这些类以创建数据库映射。首先,我们创建 SkillTitleConfiguration 类。

SkillTitleConfiguration.cs

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using QuantumWeb.Model;

namespace QuantumWeb.Data
{
    public class SkillTitleConfiguration : IEntityTypeConfiguration<SkillTitle>
    {
        public void Configure(EntityTypeBuilder<SkillTitle> builder)
        {
            builder.ToTable("SkillTitles");
            builder.HasKey(st => st.SkillCode);
            builder.Property(st => st.SkillCode)
            .HasColumnType("nvarchar(20)")
            .HasConversion(
                st => st.ToString(),
                st => (Skill)Enum.Parse(typeof(Skill), st));
            builder.Property(st => st.Title)
            .IsRequired()
            .HasColumnType("nvarchar(50)")
            .HasMaxLength(50);
        } // end public void Configure(EntityTypeBuilder<SkillTitle> builder)

    } // end public class SkillTitleConfiguration : IEntityTypeConfiguration<SkillTitle>

} // end namespace QuantumWeb.Data

这会将 SkillTitle 类映射到 SkillTitles 数据库表。它还配置了 Skill enum 值与 SkillTitles 表中的 SkillCode string 列值之间的转换。这与第三部分讨论的 ProjectState enum 值到 ProjectStateCode 值的映射相似。

现在我们可以配置 ProjectSkill 类到 joinProjectSkills 的映射。

ProjectSkillConfiguration.cs

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using QuantumWeb.Model;

namespace QuantumWeb.Data
{
    public class ProjectSkillConfiguration : IEntityTypeConfiguration<ProjectSkill>
    {
        public void Configure(EntityTypeBuilder<ProjectSkill> builder)
        {
            builder.ToTable("ProjectSkills");
            builder.HasKey(ps => ps.ProjectSkillId);
            builder.Property(ps => ps.ProjectSkillId)
            .HasColumnType("int");
            builder.Property(ps => ps.ProjectId)
            .HasColumnType("int");
            builder.Property(ps => ps.SkillCode)
            .HasColumnType("nvarchar(20)")
            .HasConversion(
                ps => ps.ToString(),
                ps => (Skill)Enum.Parse(typeof(Skill), ps));
            builder.HasIndex(ps => new { ps.ProjectId, ps.SkillCode })
            .IsUnique(false);
            builder.HasOne<Project>(ps => ps.Project)
                .WithMany(p => p.ProjectSkills)
                .HasForeignKey(ps => ps.ProjectId);
            builder.HasOne<SkillTitle>(ps => ps.SkillTitle)
                .WithMany(p => p.ProjectSkills)
                .HasForeignKey(ps => ps.SkillCode);
        } // end public void Configure(EntityTypeBuilder<ProjectSkill> builder)

    } // end public class ProjectSkillConfiguration : IEntityTypeConfiguration<ProjectSkill>

} // 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; }

        /// <summary>
        /// SkillTitle DbSet
        /// </summary>
        public DbSet<SkillTitle> SkillTitles { get; set; }

        /// <summary>
        /// ProjectSkill DbSet
        /// </summary>
        public DbSet<ProjectSkill> ProjectSkills { 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());
            modelBuilder.ApplyConfiguration(new SkillTitleConfiguration());
            modelBuilder.ApplyConfiguration(new ProjectSkillConfiguration());
        } // end  protected override void OnModelCreating(ModelBuilder modelBuilder)

    } // end public class QuantumDbContext : DbContext

} // end namespace QuantumWeb.Data

在这里,我们定义了 DbSets,即 SkillTitlesProjectSkills,并在 OnModelCreating 方法中添加了 SkillTitleConfigurationProjectSkillConfiguration 类进行处理。现在我们在程序包管理器控制台中创建一个迁移并更新数据库。

Add-Migration Add-Project-Skill-Entities

生成的 20181025222456_Add-Project-Skill-Entities.cs

using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;

namespace QuantumWeb.Migrations
{
    public partial class AddProjectSkillEntities : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "SkillTitles",
                columns: table => new
                {
                    SkillCode = table.Column<string>(type: "nvarchar(20)", nullable: false),
                    Title = table.Column<string>(type: "nvarchar(50)", 
                            maxLength: 50, nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_SkillTitles", x => x.SkillCode);
                });

            migrationBuilder.CreateTable(
                name: "ProjectSkills",
                columns: table => new
                {
                    ProjectSkillId = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:ValueGenerationStrategy",
                            SqlServerValueGenerationStrategy.IdentityColumn),
                    ProjectId = table.Column<int>(type: "int", nullable: false),
                    SkillCode = table.Column<string>(type: "nvarchar(20)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_ProjectSkills", x => x.ProjectSkillId);
                    table.ForeignKey(
                        name: "FK_ProjectSkills_Projects_ProjectId",
                        column: x => x.ProjectId,
                        principalTable: "Projects",
                        principalColumn: "ProjectId",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_ProjectSkills_SkillTitles_SkillCode",
                        column: x => x.SkillCode,
                        principalTable: "SkillTitles",
                        principalColumn: "SkillCode",
                        onDelete: ReferentialAction.Cascade);
                });

            migrationBuilder.CreateIndex(
                name: "IX_ProjectSkills_SkillCode",
                table: "ProjectSkills",
                column: "SkillCode");

            migrationBuilder.CreateIndex(
                name: "IX_ProjectSkills_ProjectId_SkillCode",
                table: "ProjectSkills",
                columns: new[] { "ProjectId", "SkillCode" });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "ProjectSkills");

            migrationBuilder.DropTable(
                name: "SkillTitles");
        }
    }
}

有几个关键点需要考虑。

  1. SkillTitles 表有一个主键 SkillCode,它映射到 Skill enum 中的一个值。
  2. ProjectSkills 表有一个整数主键 ProjectSkillId,以及两个外键 ProjectId(链接到 Project 记录)和 SkillCode(链接到 SkillTitle 记录)。一些作者建议设置一个复合主键 ProjectIdSkillCode。但这会给一个 Project 需要多种相同类型的技能带来一些问题。不过,我们设置了一个非唯一索引 IX_ProjectSkills_ProjectId_SkillCode

稍后我们将看到其中一些功能。现在将迁移应用到数据库。

Update-Database

下面由 SQL Server Management Studio (SSMS) 生成的图表显示了相关的数据库表。

Customers-Projects-Skills 数据库图

现在我们将这些实体包含在 UI 中。首先,我们在 CustomerProjects Index 页面中添加一个指向 ProjectSkills 的链接。

修改后的 CustomerProject.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="./CustomerProjectSkills" 
                        asp-route-id="@item.ProjectId">
                            ProjectSkills
                        </a> |
                        <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>

指向 CustomerProjectSkills 的链接需要我们为新的 Razor 页面生成脚手架。

为 Customers/CustomerProjectSkills Razor 页面生成脚手架

初始 ~Pages\Customers\CustomerProjectSkills.cshtml

@page "{id:int?}"
@model QuantumWeb.Pages.Customers.CustomerProjectSkillsModel
@{
    ViewData["Title"] = "Customer Project Skills";
}

<h2>Customer Project Skills</h2>

<p>
    <a asp-page="./CustomerProjects" 
     asp-route-id="@Model.Customer.CustomerId">Back to List</a> |
    <a asp-page="CustomerProjectSkillAssign" 
     asp-route-id="@Model.Project.ProjectId">Assign Skill</a>
</p>
<div>
    <h4>Customer-Project</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.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.ProjectSkills)
        </dt>
        <dd>
            <table class="table">
                <tr>
                    <th>Project Skill Id</th>
                    <th>Skill Code</th>
                    <th>Skill Title</th>
                    <th></th>
                </tr>
                @foreach (var item in Model.Project.ProjectSkills)
                {
                    <tr>
                        <td>
                            @Html.DisplayFor(modelItem => item.ProjectSkillId)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.SkillCode)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.SkillTitle.Title)
                        </td>
                        <td>
                            <a asp-page="./CustomerProjectSkillDelete"
                              asp-route-id="@item.ProjectSkillId">
                                  Delete
                            </a>
                        </td>
                    </tr>
                }
            </table>
        </dd>
    </dl>
</div>

这会将页面配置为使用可空的参数 id,允许将目标 ProjectProjectId 传递给 OnGet 处理程序。有一个链接指向 CustomerProjectSkillAssign 页面,该页面将创建一个 ProjectSkill 记录,有效地将一个 Skill 分配给 Project。还有一个链接指向 CustomerProjectSkillDelete 页面,该页面将删除一个 ProjectSkill 记录。我们稍后将展示这两个页面的代码。

初始 ~Pages\Customers\CustomerProjectSkills.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 CustomerProjectSkillsModel : PageModel
    {

        private readonly QuantumDbContext _context;

        public CustomerProjectSkillsModel(QuantumDbContext context)
        {
            _context = context;
        } // end public CustomerProjectSkillsModel(QuantumDbContext context)

        public Project Project { get; set; }
        public Customer Customer { 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)
                .Include(p => p.ProjectSkills)
                    .ThenInclude(ps => ps.SkillTitle)
                    .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)

    } // end public class CustomerProjectSkillsModel : PageModel

} // end namespace QuantumWeb.Pages.Customers

请注意 Entity Framework (EF) 预加载的示例。

            Project = await _context.Projects
                .Include(p => p.Customer)
                .Include(p => p.ProjectSkills)
                    .ThenInclude(ps => ps.SkillTitle)
                    .FirstOrDefaultAsync(p => p.ProjectId == id);

在 EF 中,使用 Include()ThenInclude 扩展方法,在一个查询中检索相关的实体。在这里,我们检索与 ProjectId 值等于输入参数 idProject 相关的 CustomerProjectSkills 实体。使用 ThenInclude() 方法,查询会进一步检索与检索到的 ProjectSkills 关联的 SkillTitles

接下来的几个屏幕截图显示了导航到此页面的顺序。

QuantumWeb Application 主页:https//: 44306/

QuantumWeb Application Customer Index Page:https//: 44306/Customers

Mirarex Oil & Gas 的 Customer Projects 页面(有 2 个项目)(已添加 ProjectSkills 链接)

Mirarex Oil & Gas, Zolar Pipeline 的 Customer Project Skills 页面(无技能)

现在我们展示 CustomerProjectSkillAssign Razor 页面的脚手架。

为 Customers/CustomerProjectSkillAssign Razor 页面生成脚手架

初始 ~Pages\Customers\CustomerProjectSkillAssign.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 CustomerProjectSkillAssignModel : PageModel
    {
        private readonly QuantumDbContext _context;

        public CustomerProjectSkillAssignModel(QuantumDbContext context)
        {
            _context = context;
        } // end public CustomerProjectSkillAssignModel(QuantumDbContext context)

        [BindProperty]
        public Project Project { get; set; }
        [BindProperty]
        public Customer Customer { get; set; }

        [BindProperty]
        public SkillTitle SkillTitle { 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)
                .Include(p => p.ProjectSkills)
                    .ThenInclude(ps => ps.SkillTitle)
                    .FirstOrDefaultAsync(p => p.ProjectId == id);

            if (Project == null)
            {
                return NotFound();
            } // endif (Project == null)

            Customer = Project.Customer;

            ViewData["SkillCode"] = new SelectList(_context.SkillTitles, "SkillCode", "Title");

            return Page();
        }// end public async Task<IActionResult> OnGet(int? id)

        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            } // endif (!ModelState.IsValid)

            ProjectSkill projectSkill = new ProjectSkill()
            {
                ProjectId = Project.ProjectId,
                SkillCode = SkillTitle.SkillCode
            };

            _context.ProjectSkills.Add(projectSkill);
            await _context.SaveChangesAsync();

            return RedirectToPage("./CustomerProjectSkills", new { id = Project.ProjectId });
        } // end public async Task<IActionResult> OnPostAsync()

    } // end public class CustomerProjectSkillAssignModel : PageModel

} // end namespace QuantumWeb.Pages.Customers

这里同样,我们有一个 ViewData 来设置一个选择列表,就像在第三部分中一样。

ViewData["SkillCode"] = new SelectList(_context.SkillTitles, "SkillCode", "Title");

这次,它处理 SkillTitles,用于选择要与 Project 关联的 Skills。

初始 ~Pages\Customers\CustomerProjectSkillAssign.cshtml

@page "{id:int?}"
@model QuantumWeb.Pages.Customers.CustomerProjectSkillAssignModel
@{
    ViewData["Title"] = "Customer Project Skill Assignment";
}

<h2>Customer Project Skill Assignment</h2>
<hr />
<dl class="dl-horizontal">
    <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.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="Project.ProjectId" />
            <div class="form-group">
                <label asp-for="SkillTitle.SkillCode" 
                class="control-label"></label>
                <select asp-for="SkillTitle.SkillCode" 
                class="form-control" asp-items="ViewBag.SkillCode"></select>
            </div>
            <div class="form-group">
                <a asp-page="./CustomerProjectSkills" 
                asp-route-id="Project.ProjectId">Project Skills</a> |
                <input type="submit" value="Assign" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>

Zolar Pipeline 的 Customer Project Skill Assignment 页面(Skill 选择打开)

Zolar Pipeline 的 Customer Project Skill Assignment 页面(分配一个程序员)

Mirarex Oil & Gas, Zolar Pipeline 的 Customer Project Skills 页面(已分配程序员)

分配了几个技能后,此页面显示如下:

Mirarex Oil & Gas, Zolar Pipeline 的 Customer Project Skills 页面(多个技能)

管理层决定分配的程序员太多,因此我们需要删除一个。现在我们将为 CustomerProjectSkillDelete 页面生成脚手架。

为 Customers/CustomerProjectSkillDelete Razor 页面生成脚手架

初始 ~Pages\Customers\CustomerProjectSkillDelete.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 CustomerProjectSkillDeleteModel : PageModel
    {
        private readonly QuantumDbContext _context;

        public CustomerProjectSkillDeleteModel(QuantumDbContext context)
        {
            _context = context;
        } // end public CustomerProjectSkillDeleteModel(QuantumDbContext 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("./CustomerProjectSkills", 
                                    new { id = Project.Customer.CustomerId });
        } // end public async Task<IActionResult> OnPostAsync(int? id)

    } // end public class CustomerProjectSkillDeleteModel : PageModel

} // end namespace QuantumWeb.Pages.Customers

初始 ~Pages\Customers\CustomerProjectSkillDelete.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, Zolar Pipeline 的 Customer Project Skill Delete 页面

Mirarex Oil & Gas, Zolar Pipeline 的 Customer Project Skills 页面(记录已删除)

摘要

我们已经实现了 Skill 到 Project 的多对多关系以及 join 实体 ProjectSkill,以便在 Entity Framework Core 中实现该功能。我们还创建了 ASP.NET Razor Pages 来管理这些实体。

© . All rights reserved.