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





5.00/5 (7投票s)
添加项目和项目技能处理
引言
这是多部分文章的第四部分,演示了如何通过 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 enum
、SkillTitle
和 ProjectSkill
)并实现 Projects 和 Skills 之间的多对多关系,如下所示:
- 添加实体
Skill
、SkillTitle
和ProjectSkill
,并配置到QuantumDbContext
中。 - 添加迁移、Add-Project-Skill-Entities,并更新数据库。
- 为 Skill 相关 Razor 页面生成脚手架,CustomerProjectSkills.cshtml、CustomerProjectSkillAssign.cshtml 和 CustomerProjectSkillDelete.cshtml。运行测试以确认功能。
背景
本系列中实现的示例应用程序是为虚构的工程技术公司 Quantum Engineering Technologies 制作的。Quantum 主要服务于石油、天然气和化学工业。到目前为止,我们已经讨论了对象模型的基础,并创建了许多 ASP.NET Razor Pages 来处理 Customers
、Projects
和 Project
状态。在最后一部分,我们将讨论执行工作所需的技能列表。这项工作围绕 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
实例的引用。ProjectId
和 SkillCode
属性将映射到数据库中 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
类到 join
表 ProjectSkills
的映射。
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
,即 SkillTitles
和 ProjectSkills
,并在 OnModelCreating
方法中添加了 SkillTitleConfiguration
和 ProjectSkillConfiguration
类进行处理。现在我们在程序包管理器控制台中创建一个迁移并更新数据库。
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");
}
}
}
有几个关键点需要考虑。
SkillTitles
表有一个主键SkillCode
,它映射到Skill enum
中的一个值。ProjectSkills
表有一个整数主键ProjectSkillId
,以及两个外键ProjectId
(链接到Project
记录)和SkillCode
(链接到SkillTitle
记录)。一些作者建议设置一个复合主键ProjectId
和SkillCode
。但这会给一个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
,允许将目标 Project
的 ProjectId
传递给 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
值等于输入参数 id
的 Project
相关的 Customer
和 ProjectSkills
实体。使用 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 来管理这些实体。