使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

目录

介绍

使用代码


添加项目和项目状态处理

介绍

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

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

第一部分。设置实体框架数据上下文和初始Customer的Razor页面

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

在第三部分。我们将创建Project和ProjectState实体并在ProjectState和Project之间实现一个一对多的关系,如下:

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

使用代码

添加初始项目处理。

接下来,我们启用Customer项目处理。该应用程序使用Customer作为“网关”实体; 一切都是通过Customer。Customer和Projects之间存在一对多的关系。因此,我们需要修改Customer类。

修改后的Customer.cs

  1.   using System.Collections.Generic;
  2.    
  3.   namespace QuantumWeb.Model
  4.   {
  5.   /// <summary>
  6.   /// Customer Class
  7.   /// </summary>
  8.   public class Customer
  9.   {
  10.   #region Constructors
  11.    
  12.   /// <summary>
  13.   /// Parameter-less Constructor
  14.   /// </summary>
  15.   /// <remarks>
  16.   /// Required for scaffolding the UI
  17.   /// </remarks>
  18.   public Customer()
  19.   {
  20.   } // end public Customer()
  21.    
  22.   #endregion // Constructors
  23.    
  24.   /// <summary>
  25.   /// Customer Identifier, primary key
  26.   /// </summary>
  27.   public int CustomerId { get; set; }
  28.   /// <summary>
  29.   /// Customer Name
  30.   /// </summary>
  31.   public string CustomerName { get; set; }
  32.   /// <summary>
  33.   /// Primary Customer Contact
  34.   /// </summary>
  35.   public string CustomerContact { get; set; }
  36.   /// <summary>
  37.   /// Customer Contact Phone Number
  38.   /// </summary>
  39.   public string CustomerPhone { get; set; }
  40.   /// <summary>
  41.   /// Customer Contact Email Address
  42.   /// </summary>
  43.   public string CustomerEmail { get; set; }
  44.    
  45.   #region Navigation Properties
  46.    
  47.   /// <summary>
  48.   /// List of Projects
  49.   /// </summary>
  50.   public List<Project> Projects { get; set; }
  51.    
  52.   #endregion // Navigation Properties
  53.    
  54.   } // end public class Customer
  55.    
  56.   } // end namespace QuantumWeb.Model

我们添加了一个项目列表。在这里,我们将某些属性标识为导航属性。这些属性引用其他类/实体,以便我们可以在处理中导航到它们。Customer在Projects列表中表示零或多个Projects。初始Project类定义如下。

初始Project.cs

  1.   namespace QuantumWeb.Model
  2.   {
  3.   /// <summary>
  4.   /// Project Class
  5.   /// </summary>
  6.   public class Project
  7.   {
  8.   /// <summary>
  9.   /// Project Identifier, primary key
  10.   /// </summary>
  11.   public int ProjectId { get; set; }
  12.   /// <summary>
  13.   /// Project Name
  14.   /// </summary>
  15.   public string ProjectName { get; set; }
  16.    
  17.   #region Navigation Properties
  18.    
  19.   /// <summary>
  20.   /// Customer Identifier
  21.   /// </summary>
  22.   public int CustomerId { get; set; }
  23.    
  24.   /// <summary>
  25.   /// Customer
  26.   /// </summary>
  27.   /// <remarks>
  28.   /// Every Project has a Customer
  29.   /// </remarks>
  30.   public Customer Customer { get; set; }
  31.    
  32.   /// <summary>
  33.   /// Project Status Code
  34.   /// </summary>
  35.   public ProjectState ProjectStateCode { get; set; }
  36.    
  37.   /// <summary>
  38.   /// ProjectStateDescription Reference
  39.   /// </summary>
  40.   public ProjectStateDescription ProjectStateDescription { get; set; }
  41.    
  42.   #endregion // Navigation Properties
  43.    
  44.   } // end public class Project
  45.    
  46.   } // end namespace QuantumApp.Model

除了定义初始化Project类之外,我们还将在Model文件夹中定义ProjectState enum。

ProjectState.cs

  1.   namespace QuantumWeb.Model
  2.   {
  3.   /// <summary>
  4.   /// Project State Enumeration
  5.   /// </summary>
  6.   public enum ProjectState
  7.   {
  8.   Prospect,
  9.   UnderReview,
  10.   StartScheduled,
  11.   InProgress,
  12.   Completed
  13.   } // end public enum ProjectState
  14.    
  15.   } // end namespace QuantumWeb.Model

这个enum指定了Project工作流的状态。

  • Prospect。这涉及一个有前景的Project。这个Project可能是通过推荐或其他营销工作提出的。尚未进行任何研究,且规格尚不清楚。
  • UnderReview。在这种状态下,Project制定了要求,初始预算和进度表。没有承诺Quantum或者Customer。
  • StartScheduled。已经指定了工作开始的日期,并且正在准备开始工作。
  • InProgress。实际工作已经开始但尚未完成。
  • Completed。项目工作完成。

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

  1. 我们应该为Project将在UI中显示的每个状态定义简短描述,以帮助用户理解每个状态的含义。
  2. 每个enum值都作为string类型存储在数据库中。

为了满足ProjectState enum的这些要求,我们定义了ProjectStateDescription类。

ProjectStateDescription.cs

  1.   using System.Collections.Generic;
  2.    
  3.   namespace QuantumWeb.Model
  4.   {
  5.   /// <summary>
  6.   /// Project State Description Class
  7.   /// </summary>
  8.   public class ProjectStateDescription
  9.   {
  10.   /// <summary>
  11.   /// ProjectState Code
  12.   /// </summary>
  13.   public ProjectState ProjectStateCode { get; set; }
  14.    
  15.   /// <summary>
  16.   /// State Description
  17.   /// </summary>
  18.   public string StateDescription { get; set; }
  19.    
  20.   #region Navigation Properties
  21.    
  22.   /// <summary>
  23.   /// Projects Collection
  24.   /// </summary>
  25.   public List<Project> Projects { get; set; }
  26.    
  27.   #endregion // Navigation Properties
  28.    
  29.   } // end public class ProjectStateDescription
  30.    
  31.   } // end namespace QuantumWeb.Model

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

接下来,我们需要为Project和ProjectStateDescription定义EF配置类,并在QuantumDbContext类中包含所有内容。所有此活动都发生在Data文件夹中。

初始ProjectConfiguration.cs

  1.   using System;
  2.   using Microsoft.EntityFrameworkCore;
  3.   using Microsoft.EntityFrameworkCore.Metadata.Builders;
  4.   using QuantumWeb.Model;
  5.    
  6.   namespace QuantumWeb.Data
  7.   {
  8.   public class ProjectConfiguration : IEntityTypeConfiguration<Project>
  9.   {
  10.   public void Configure(EntityTypeBuilder<Project> builder)
  11.   {
  12.   builder.ToTable("Projects");
  13.   builder.HasKey(p => p.ProjectId);
  14.   builder.Property(p => p.ProjectId)
  15.   .HasColumnType("int");
  16.   builder.Property(p => p.ProjectName)
  17.   .IsRequired()
  18.   .HasColumnType("nvarchar(80)")
  19.   .HasMaxLength(80);
  20.   builder.Property(p => p.CustomerId)
  21.   .HasColumnType("int")
  22.   .IsRequired();
  23.   builder.HasOne(p => p.Customer)
  24.   .WithMany(c => c.Projects)
  25.   .HasForeignKey(p => p.CustomerId)
  26.   .IsRequired();
  27.   builder.Property(p => p.ProjectStateCode)
  28.   .HasColumnType("nvarchar(15)")
  29.   .HasDefaultValue(ProjectState.Prospect)
  30.   .HasConversion(
  31.   p => p.ToString(),
  32.   p => (ProjectState)Enum.Parse(typeof(ProjectState), p));
  33.   builder.HasOne(p => p.ProjectStateDescription)
  34.   .WithMany(pd => pd.Projects)
  35.   .HasForeignKey(p => p.ProjectStateCode);
  36.   } // end public void Configure(EntityTypeBuilder<Project> builder)
  37.    
  38.   } // end public class ProjectConfiguration : IEntityTypeConfiguration<Project>
  39.    
  40.   } // end namespace QuantumWeb.Data

看看下面提取的行:

  1.   builder.HasOne(p => p.Customer)
  2.   .WithMany(c => c.Projects)
  3.   .HasForeignKey(p => p.CustomerId)
  4.   .IsRequired();

对这些行的解释是,“每个Project都有一个带有许多Projects的Customer。每个Project都映射到Projects数据库中的表,通过外键CustomerId,并且这是必需的。因此,Customer- Project关系是一对多。

在一对多ProjectStateDescription- Project关系被配置为:

  1.   builder.HasOne(p => p.ProjectStateDescription)
  2.   .WithMany(pd => pd.Projects)
  3.   .HasForeignKey(p => p.ProjectStateCode);

接下来,我们将了解处理enum的值到数据库string列配置的方式。

  1.   builder.Property(p => p.ProjectStateCode)
  2.   .HasColumnType("nvarchar(15)")
  3.   .HasDefaultValue(ProjectState.Prospect)
  4.   .HasConversion(
  5.   p => p.ToString(),
  6.   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

  1.   using System;
  2.   using Microsoft.EntityFrameworkCore;
  3.   using Microsoft.EntityFrameworkCore.Metadata.Builders;
  4.   using QuantumWeb.Model;
  5.    
  6.   namespace QuantumWeb.Data
  7.   {
  8.   /// <summary>
  9.   /// ProjectState Description Configuration Class
  10.   /// </summary>
  11.   public class ProjectStateDescriptionConfiguration :
  12.   IEntityTypeConfiguration<ProjectStateDescription>
  13.   {
  14.    
  15.   public void Configure(EntityTypeBuilder<ProjectStateDescription> builder)
  16.   {
  17.   builder.ToTable("ProjectStateDescriptions");
  18.   builder.HasKey(p => p.ProjectStateCode);
  19.   builder.Property(p => p.ProjectStateCode)
  20.   .HasColumnType("nvarchar(15)")
  21.   .HasConversion(
  22.   p => p.ToString(),
  23.   p => (ProjectState)Enum.Parse(typeof(ProjectState), p));
  24.   builder.Property(p => p.StateDescription)
  25.   .IsRequired()
  26.   .HasColumnType("nvarchar(80)")
  27.   .HasMaxLength(80);
  28.   } // end public void Configure(EntityTypeBuilder<ProjectStateDescription> builder)
  29.    
  30.   } // end public class ProjectStateDescriptionConfiguration :
  31.   // IEntityTypeConfiguration<ProjectStateDescription>
  32.    
  33.   } // end namespace QuantumWeb.Data

现在,我们更新QuantumDbContext类。

然后更新QuantumDbContext.cs

  1.   using Microsoft.EntityFrameworkCore;
  2.   using QuantumWeb.Model;
  3.    
  4.   namespace QuantumWeb.Data
  5.   {
  6.   public class QuantumDbContext : DbContext
  7.   {
  8.   public QuantumDbContext (DbContextOptions<QuantumDbContext> options)
  9.   : base(options)
  10.   {
  11.   } // end public QuantumDbContext (DbContextOptions<QuantumDbContext> options)
  12.    
  13.   #region DbSets
  14.    
  15.   /// <summary>
  16.   /// Customer DbSet
  17.   /// </summary>
  18.   public DbSet<Customer> Customers { get; set; }
  19.    
  20.   /// <summary>
  21.   /// Project DbSet
  22.   /// </summary>
  23.   public DbSet<Project> Projects { get; set; }
  24.    
  25.   /// <summary>
  26.   /// ProjectStateDescription DbSet
  27.   /// </summary>
  28.   public DbSet<ProjectStateDescription> ProjectStateDescriptions { get; set; }
  29.    
  30.   #endregion // DbSets
  31.    
  32.   /// <summary>
  33.   /// Data Model Creation Method
  34.   /// </summary>
  35.   /// <param name="modelBuilder">ModelBuilder instance</param>
  36.   protected override void OnModelCreating(ModelBuilder modelBuilder)
  37.   {
  38.   modelBuilder.ApplyConfiguration(new CustomerConfiguration());
  39.   modelBuilder.ApplyConfiguration(new ProjectConfiguration());
  40.   modelBuilder.ApplyConfiguration(new ProjectStateDescriptionConfiguration());
  41.   } // end protected override void OnModelCreating(ModelBuilder modelBuilder)
  42.    
  43.   } // end public class QuantumDbContext : DbContext
  44.    
  45.   } // end namespace QuantumWeb.Data

现在为Project和ProjectState实体添加EF迁移。(译者注:同样需要调整appsettings.json中数据库连接字符串,建议删除Migrations文件夹后在执行下面的命令,同时提醒,在执行下面命令后需要命令update-database更新到数据库)

Add-Migration Added-Project-ProjectState

生成 ~\Migrations\20181021203503_Added-Project-ProjectState.cs:

  1.   using Microsoft.EntityFrameworkCore.Metadata;
  2.   using Microsoft.EntityFrameworkCore.Migrations;
  3.    
  4.   namespace QuantumWeb.Migrations
  5.   {
  6.   public partial class AddedProjectProjectState : Migration
  7.   {
  8.   protected override void Up(MigrationBuilder migrationBuilder)
  9.   {
  10.   migrationBuilder.CreateTable(
  11.   name: "ProjectStateDescriptions",
  12.   columns: table => new
  13.   {
  14.   ProjectStateCode =
  15.   table.Column<string>(type: "nvarchar(15)", nullable: false),
  16.   StateDescription =
  17.   table.Column<string>(type: "nvarchar(80)", maxLength: 80, nullable: false)
  18.   },
  19.   constraints: table =>
  20.   {
  21.   table.PrimaryKey("PK_ProjectStateDescriptions", x => x.ProjectStateCode);
  22.   });
  23.    
  24.   migrationBuilder.CreateTable(
  25.   name: "Projects",
  26.   columns: table => new
  27.   {
  28.   ProjectId = table.Column<int>(type: "int", nullable: false)
  29.   .Annotation("SqlServer:ValueGenerationStrategy",
  30.   SqlServerValueGenerationStrategy.IdentityColumn),
  31.   ProjectName = table.Column<string>(type: "nvarchar(80)",
  32.   maxLength: 80, nullable: false),
  33.   CustomerId = table.Column<int>(type: "int", nullable: false),
  34.   ProjectStateCode = table.Column<string>
  35.   (type: "nvarchar(15)", nullable: false, defaultValue: "Prospect")
  36.   },
  37.   constraints: table =>
  38.   {
  39.   table.PrimaryKey("PK_Projects", x => x.ProjectId);
  40.   table.ForeignKey(
  41.   name: "FK_Projects_Customers_CustomerId",
  42.   column: x => x.CustomerId,
  43.   principalTable: "Customers",
  44.   principalColumn: "CustomerId",
  45.   onDelete: ReferentialAction.Cascade);
  46.   table.ForeignKey(
  47.   name: "FK_Projects_ProjectStateDescriptions_ProjectStateCode",
  48.   column: x => x.ProjectStateCode,
  49.   principalTable: "ProjectStateDescriptions",
  50.   principalColumn: "ProjectStateCode",
  51.   onDelete: ReferentialAction.Cascade);
  52.   });
  53.    
  54.   migrationBuilder.CreateIndex(
  55.   name: "IX_Projects_CustomerId",
  56.   table: "Projects",
  57.   column: "CustomerId");
  58.    
  59.   migrationBuilder.CreateIndex(
  60.   name: "IX_Projects_ProjectStateCode",
  61.   table: "Projects",
  62.   column: "ProjectStateCode");
  63.   }
  64.    
  65.   protected override void Down(MigrationBuilder migrationBuilder)
  66.   {
  67.   migrationBuilder.DropTable(
  68.   name: "Projects");
  69.    
  70.   migrationBuilder.DropTable(
  71.   name: "ProjectStateDescriptions");
  72.   }
  73.   }
  74.   }

在Update- Database命令之后,SQL Server Management Studio(SSMS)中的数据库关系图如下所示。

使用Customer- Project- ProjectState表的QuantumDbContext数据库关系图:

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

修改Project和ProjectState的Razor 页。

我们需要为项目的应用程序添加一些自定义客户Razor 页面。首先,我们需要为CustomerProjects添加一个指向Customer/Index页面的链接。

添加CustomerProjects 链接指向Pages\Customers\Index.cshtml:

  1.   @page
  2.   @model QuantumWeb.Pages.Customers.IndexModel
  3.    
  4.   @{
  5.   ViewData["Title"] = "Index";
  6.   }
  7.    
  8.   <h2>Index</h2>
  9.    
  10.   <p>
  11.   <a asp-page="Create">Create New</a>
  12.   <!-- A link to the Pages/Customers/Create page to create a new Customer -->
  13.   </p>
  14.   <!-- An HTML table to display existing Customers -->
  15.   <table class="table">
  16.   <thead>
  17.   <tr>
  18.   <th>
  19.   @Html.DisplayNameFor(model => model.Customer[0].CustomerName)
  20.   </th>
  21.   <th>
  22.   @Html.DisplayNameFor(model => model.Customer[0].CustomerContact)
  23.   </th>
  24.   <th>
  25.   @Html.DisplayNameFor(model => model.Customer[0].CustomerPhone)
  26.   </th>
  27.   <th>
  28.   @Html.DisplayNameFor(model => model.Customer[0].CustomerEmail)
  29.   </th>
  30.   <th></th>
  31.   </tr>
  32.   </thead>
  33.   <tbody>
  34.   @foreach (var item in Model.Customer) {
  35.   <tr>
  36.   <td>
  37.   @Html.DisplayFor(modelItem => item.CustomerName)
  38.   </td>
  39.   <td>
  40.   @Html.DisplayFor(modelItem => item.CustomerContact)
  41.   </td>
  42.   <td>
  43.   @Html.DisplayFor(modelItem => item.CustomerPhone)
  44.   </td>
  45.   <td>
  46.   @Html.DisplayFor(modelItem => item.CustomerEmail)
  47.   </td>
  48.   <td>
  49.   <a asp-page="./Edit" asp-route-id="@item.CustomerId">Edit</a> |
  50.   <!-- A link to the Pages/Customers/Edit page to edit an existing Customer -->
  51.   <a asp-page="./Details" asp-route-id="@item.CustomerId">Details</a> |
  52.   <!--
  53.   A link to the Pages/Customers/Details page to display the details for an existing
  54.   Customer
  55.   -->
  56.   <a asp-page="./CustomerProjects" asp-route-id="@item.CustomerId">Projects</a> |
  57.   <!--
  58.   A link to the Pages/Customers/CustomerProjects page to display & manage the
  59.   Projects for an existing Customer
  60.   -->
  61.   <a asp-page="./Delete" asp-route-id="@item.CustomerId">Delete</a>
  62.   <!-- A link to the Pages/Customers/Delete page to delete an existing Customer -->
  63.   </td>
  64.   </tr>
  65.   }
  66.   </tbody>
  67.   </table>

我们将如下构建几个自定义Customers Razor页面。

为客户设计的定制构建Razor页面:

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

搭建Customers/CustomerProjects Razor 页面:

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

单击“ 添加 ”将为CustomerProjects Index页面生成shell文件。

生成~Pages\Customers\CustomerProjects.cshtml

  1.   @page
  2.   @model QuantumWeb.Pages.Customers.CustomerProjectsModel
  3.   @{
  4.   ViewData["Title"] = "CustomerProjects";
  5.   }
  6.    
  7.   <h2>CustomerProjects</h2>

生成~Pages\Customers\CustomerProjects.cshtml.cs

  1.   using System;
  2.   using System.Collections.Generic;
  3.   using System.Linq;
  4.   using System.Threading.Tasks;
  5.   using Microsoft.AspNetCore.Mvc;
  6.   using Microsoft.AspNetCore.Mvc.RazorPages;
  7.    
  8.   namespace QuantumWeb.Pages.Customers
  9.   {
  10.   public class CustomerProjectsModel : PageModel
  11.   {
  12.   public void OnGet()
  13.   {
  14.    
  15.   }
  16.   }
  17.   }

我们将在每种情况下修改这些shell文件以满足我们的需求。CustomerProjects Index 页面的修改文件。

修改了~Pages\Customers\CustomerProjects.cshtml

  1.   @page "{id:int?}"
  2.   @model QuantumWeb.Pages.Customers.CustomerProjectsModel
  3.   @{
  4.   ViewData["Title"] = "Customer Projects";
  5.   }
  6.    
  7.   <h2>Customer Projects</h2>
  8.    
  9.   <div>
  10.   <h4>Customer</h4>
  11.   <hr />
  12.   <dl class="dl-horizontal">
  13.   <dt>
  14.   @Html.DisplayNameFor(model => model.Customer.CustomerId)
  15.   </dt>
  16.   <dd>
  17.   @Html.DisplayFor(model => model.Customer.CustomerId)
  18.   </dd>
  19.   <dt>
  20.   @Html.DisplayNameFor(model => model.Customer.CustomerName)
  21.   </dt>
  22.   <dd>
  23.   @Html.DisplayFor(model => model.Customer.CustomerName)
  24.   </dd>
  25.   <dt>
  26.   @Html.DisplayNameFor(model => model.Customer.Projects)
  27.   </dt>
  28.   <dd>
  29.   <table class="table">
  30.   <tr>
  31.   <th>Project ID</th>
  32.   <th>Project Name</th>
  33.   <th>Project State</th>
  34.   <th></th>
  35.   </tr>
  36.   @foreach (var item in Model.Customer.Projects)
  37.   {
  38.   <tr>
  39.   <td>
  40.   @Html.DisplayFor(modelItem => item.ProjectId)
  41.   </td>
  42.   <td>
  43.   @Html.DisplayFor(modelItem => item.ProjectName)
  44.   </td>
  45.   <td>
  46.   @Html.DisplayFor(modelItem => item.ProjectStateCode)
  47.   </td>
  48.   <td>
  49.   <a asp-page="./CustomerProjectEdit"
  50.    
  51.   asp-route-id="@item.ProjectId">Edit</a> |
  52.   <a asp-page="./CustomerProjectDelete"
  53.    
  54.   asp-route-id="@item.ProjectId">Delete</a>
  55.   </td>
  56.   </tr>
  57.   }
  58.   </table>
  59.   </dd>
  60.   </dl>
  61.   </div>
  62.    
  63.   <div>
  64.   <a asp-page="CustomerProjectCreate" asp-route-id="@Model.Customer.CustomerId">
  65.   Create New Project</a> |
  66.   <a asp-page="./Index">Back to List</a>
  67.   </div>

“ {id:int?}”表示需要整数参数,id需要或者请求页面将返回HTTP 401(未找到页面)错误。在这种情况下,这是目标Customer的标识符(CustomerId)。另外,请注意引用该CustomerProjectCreate页面的链接。

<a asp-page="CustomerProjectCreate" asp-route-id="@Model.Customer.CustomerId">Create New Project</a> |

这将把我们带到CustomerProjectCreate尚未创建的页面,为引用Customer创建一个新的Project。

修改了~Pages\Customers\CustomerProjects.cshtml.cs

  1.   using System.Threading.Tasks;
  2.   using Microsoft.AspNetCore.Mvc;
  3.   using Microsoft.AspNetCore.Mvc.RazorPages;
  4.   using Microsoft.EntityFrameworkCore;
  5.   using QuantumWeb.Data;
  6.   using QuantumWeb.Model;
  7.    
  8.   namespace QuantumWeb.Pages.Customers
  9.   {
  10.   public class CustomerProjectsModel : PageModel
  11.   {
  12.   private readonly QuantumDbContext _context;
  13.    
  14.   public CustomerProjectsModel(QuantumDbContext context)
  15.   {
  16.   _context = context;
  17.   } // end public CustomerProjectsModel(QuantumDbContext context)
  18.    
  19.   public Customer Customer { get; set; }
  20.    
  21.   public async Task<IActionResult> OnGet(int? id)
  22.   {
  23.   if (id == null)
  24.   {
  25.   return NotFound();
  26.   } // endif (id == null)
  27.    
  28.   Customer = await _context.Customers
  29.   .Include(c => c.Projects)
  30.   .FirstOrDefaultAsync(c => c.CustomerId == id);
  31.    
  32.   if (Customer == null)
  33.   {
  34.   return NotFound();
  35.   } // endif (Customer == null)
  36.    
  37.   return Page();
  38.   } // end public async Task<IActionResult> OnGet(int? id)
  39.    
  40.   } // end public class CustomerProjectsModel : PageModel
  41.    
  42.   } // end namespace QuantumWeb.Pages.Customers

请注意,OnGet处理程序具有可为空的整数参数,id应该是如上所述的CustomerId。

QuantumWeb 应用客户页面: https//localhost: 44306/Customers 具有项目链接。

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

Customer Projects页面:https//localhost: 44306/Customers/CustomerProjects/1(无项目)

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

“ 创建新项目 ”链接将激活自定义CustomerProjectCreate Razor页面。我们现在搭建这个页面。

搭建Customers/CustomerProjectCreate Razor页面:

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

Initial~Pages\Customers\CustomerProjectCreate.cshtml.cs

  1.   using System.Threading.Tasks;
  2.   using Microsoft.AspNetCore.Mvc;
  3.   using Microsoft.AspNetCore.Mvc.RazorPages;
  4.   using Microsoft.AspNetCore.Mvc.Rendering;
  5.   using Microsoft.EntityFrameworkCore;
  6.   using QuantumWeb.Data;
  7.   using QuantumWeb.Model;
  8.    
  9.   namespace QuantumWeb.Pages.Customers
  10.   {
  11.   public class CustomerProjectCreateModel : PageModel
  12.   {
  13.   private readonly QuantumDbContext _context;
  14.    
  15.   public CustomerProjectCreateModel(QuantumDbContext context)
  16.   {
  17.   _context = context;
  18.   } // end public CustomerProjectCreateModel(QuantumContext context)
  19.    
  20.   [BindProperty]
  21.   public Customer Customer { get; set; }
  22.    
  23.   public async Task<IActionResult> OnGet(int? id)
  24.   {
  25.   if (id == null)
  26.   {
  27.   return NotFound();
  28.   } // endif (id == null)
  29.    
  30.   Customer = await _context.Customers
  31.   .Include(c => c.Projects)
  32.   .FirstOrDefaultAsync(c => c.CustomerId == id);
  33.    
  34.   if (Customer == null)
  35.   {
  36.   return NotFound();
  37.   } // endif (Customer == null)
  38.    
  39.   ViewData["ProjectStateCode"] = new SelectList(_context.ProjectStateDescriptions,
  40.   "ProjectStateCode", "StateDescription", ProjectState.Prospect);
  41.    
  42.   return Page();
  43.    
  44.   } // end public async Task<IActionResult> OnGet(int? id)
  45.    
  46.   [BindProperty]
  47.   public Project Project { get; set; }
  48.    
  49.   public async Task<IActionResult> OnPostAsync()
  50.   {
  51.   if (!ModelState.IsValid)
  52.   {
  53.   return Page();
  54.   } // endif (!ModelState.IsValid)
  55.    
  56.   Project.CustomerId = Customer.CustomerId;
  57.    
  58.   _context.Projects.Add(Project);
  59.   await _context.SaveChangesAsync();
  60.    
  61.   return RedirectToPage("./CustomerProjects", new { id = Customer.CustomerId });
  62.   } // end public async Task<IActionResult> OnPostAsync()
  63.    
  64.   } // end public class CustomerProjectCreateModel : PageModel
  65.    
  66.   } // end namespace QuantumWeb.Pages.Customers

请注意此代码中的这些行。 

  1.   [BindProperty]
  2.   public Customer Customer { get; set; }

该[BindProperty]将Customer实例绑定到UI的元素,以便在浏览器和Web服务器之间保留它们的值。另请注意,此属性也适用于Project实例。

  1.   Customer = await _context.Customers
  2.   .Include(c => c.Projects)
  3.   .FirstOrDefaultAsync(c => c.CustomerId == id);

此语句对数据库执行查询,以检索Customer其主键值CustomerId与输入参数id值及其关联Project记录匹配的记录。如果有,该.Include函数的功能是查询中包含相关记录。

  1.   ViewData["ProjectStateCode"] = new SelectList(_context.ProjectStateDescriptions,
  2.   "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中默认选择的值。您可以阅读更多ViewData信息在以下链接上 https://www.tektutorialshub.com/viewbag-viewdata-asp-net-core/。(译者注:需要在ProjectStateDescriptions数据库表中手动加入数据,具体可见下图展示的数据加,ProjectStateCode字段的值就是对应枚举中的值--定义在代码中的名称。如此值为Completed,StateDescription字段值为project is complete,以此类推)

初始化~Pages\ Customers\CustomerProjectCreate.cshtml

  1.   @page
  2.   @model QuantumWeb.Pages.Customers.CustomerProjectCreateModel
  3.   @{
  4.   ViewData["Title"] = "Create Customer Project";
  5.   }
  6.    
  7.   <h2>Create Customer Project</h2>
  8.   <hr />
  9.   <dl class="dl-horizontal">
  10.   <dt>
  11.   @Html.DisplayNameFor(model => model.Customer.CustomerId)
  12.   </dt>
  13.   <dd>
  14.   @Html.DisplayFor(model => model.Customer.CustomerId)
  15.   </dd>
  16.   <dt>
  17.   @Html.DisplayNameFor(model => model.Customer.CustomerName)
  18.   </dt>
  19.   <dd>
  20.   @Html.DisplayFor(model => model.Customer.CustomerName)
  21.   </dd>
  22.   </dl>
  23.   <div class="row">
  24.   <div class="col-md-4">
  25.   <form method="post">
  26.   <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  27.   <input type="hidden" asp-for="Customer.CustomerId" />
  28.   <div class="form-group">
  29.   <label asp-for="Project.ProjectName" class="control-label"></label>
  30.   <input asp-for="Project.ProjectName" class="form-control">
  31.   </div>
  32.   <div class="form-group">
  33.   <label asp-for="Project.ProjectStateCode" class="control-label"></label>
  34.   <select asp-for="Project.ProjectStateCode" class="form-control"
  35.    
  36.   asp-items="ViewBag.ProjectStateCode">
  37.   </select>
  38.   </div>
  39.   <div class="form-group">
  40.   <input type="submit" value="Create" class="btn btn-default" />
  41.   </div>
  42.   </form>
  43.   </div>
  44.   </div>
  45.    
  46.   <div>
  47.   <a asp-page="CustomerProjects" asp-route-id="@Model.Customer.CustomerId">
  48.   Back to Customer Projects
  49.   </a>
  50.   </div>
  51.    
  52.   @section Scripts {
  53.   @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
  54.   }

关键要素如下:

<input type="hidden" asp-for="Customer.CustomerId" />

这个隐藏<input>捕获目标CustomerId以便在<form>发布时可用它来创建Project。

  1.   <select asp-for="Project.ProjectStateCode" class="form-control"
  2.   asp-items="ViewBag.ProjectStateCode">
  3.   </select>

此<select>元素将显示为UI中的下拉列表,其中包含CustomerProjectCreate.OnGet()方法中ViewData填充的值。

初始化 ~Pages\Customers\CustomerProjectCreate.cshtml

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

这将显示最初显示的Customers/CustomerProjectCreate页面。

CustomerProjectCreate 包含数据的页面:

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

点击“ Create”后,我们会看到:

客户Projects页面添加Project:

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

接下来的两个图显示了为两个Customers添加其他Projects之后的情况。

客户项目页面包含Mirarex Oil&Gas的2个项目:

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

客户项目页面包含Polyolefin Processing, Inc.的3个项目

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

我们现在可以添加另一个页面来编辑Customer项目,CustomerProjectEdit页面。

搭建 Customers/CustomerProjectEdit Razor Page

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

初始化~Pages\Customers\CustomerProjectEdit.cshtml.cs

  1.   using System.Threading.Tasks;
  2.   using Microsoft.AspNetCore.Mvc;
  3.   using Microsoft.AspNetCore.Mvc.RazorPages;
  4.   using Microsoft.AspNetCore.Mvc.Rendering;
  5.   using Microsoft.EntityFrameworkCore;
  6.   using QuantumWeb.Data;
  7.   using QuantumWeb.Model;
  8.    
  9.   namespace QuantumApp.Pages.Customers
  10.   {
  11.   public class CustomerProjectEditModel : PageModel
  12.   {
  13.   private readonly QuantumDbContext _context;
  14.    
  15.   public CustomerProjectEditModel(QuantumDbContext context)
  16.   {
  17.   _context = context;
  18.   } // end public CustomerProjectEditModel(QuantumDbContext context)
  19.    
  20.   [BindProperty]
  21.   public Customer Customer { get; set; }
  22.   [BindProperty]
  23.   public Project Project { get; set; }
  24.    
  25.   public async Task<IActionResult> OnGet(int? id)
  26.   {
  27.   if (id == null)
  28.   {
  29.   return NotFound();
  30.   } // endif (id == null)
  31.    
  32.   Project = await _context.Projects
  33.   .Include(p => p.Customer)
  34.   .FirstOrDefaultAsync(p => p.ProjectId == id);
  35.    
  36.   if (Project == null)
  37.   {
  38.   return NotFound();
  39.   } // endif (Project == null)
  40.    
  41.   Customer = Project.Customer;
  42.    
  43.   ViewData["ProjectStateCode"] = new SelectList(_context.ProjectStateDescriptions,
  44.   "ProjectStateCode", "StateDescription", ProjectState.Prospect);
  45.    
  46.   return Page();
  47.   } // end public async Task<IActionResult> OnGet(int? id)
  48.    
  49.   public async Task<IActionResult> OnPostAsync(int? id)
  50.   {
  51.   if (!ModelState.IsValid)
  52.   {
  53.   return Page();
  54.   } // endif (!ModelState.IsValid)
  55.    
  56.   var projectToUpdate = await _context.Projects.FindAsync(id);
  57.    
  58.   if (projectToUpdate == null)
  59.   {
  60.   return NotFound();
  61.   } // endif (projectToUpdate == null)
  62.    
  63.   projectToUpdate.CustomerId = Customer.CustomerId;
  64.    
  65.   if (await TryUpdateModelAsync<Project>(
  66.   projectToUpdate,
  67.   "project",
  68.   p => p.ProjectName, p => p.ProjectStateCode))
  69.   {
  70.   await _context.SaveChangesAsync();
  71.   return RedirectToPage("./CustomerProjects", new { id = Customer.CustomerId });
  72.   }
  73.    
  74.   return Page();
  75.   } // end public async Task<IActionResult> OnPostAsync(int? id)
  76.    
  77.   } // end public class CustomerProjectEditModel : PageModel
  78.    
  79.   } // end namespace QuantumApp.Pages.Customers

此代码与CustomerProjectCreate页面在.Include和ViewData方面具有相同的构件。

初始化~Pages\Customers\CustomerProjectEdit.cshtml

  1.   @page "{id:int?}"
  2.   @model QuantumWeb.Pages.Customers.CustomerProjectEditModel
  3.   @{
  4.   ViewData["Title"] = "Edit Customer Project";
  5.   }
  6.    
  7.   <h2>Edit Customer Project</h2>
  8.   <hr />
  9.   <dl class="dl-horizontal">
  10.   <dt>
  11.   @Html.DisplayNameFor(model => model.Customer.CustomerId)
  12.   </dt>
  13.   <dd>
  14.   @Html.DisplayFor(model => model.Customer.CustomerId)
  15.   </dd>
  16.   <dt>
  17.   @Html.DisplayNameFor(model => model.Customer.CustomerName)
  18.   </dt>
  19.   <dd>
  20.   @Html.DisplayFor(model => model.Customer.CustomerName)
  21.   </dd>
  22.   </dl>
  23.   <div class="row">
  24.   <div class="col-md-4">
  25.   <form method="post">
  26.   <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  27.   <input type="hidden" asp-for="Customer.CustomerId" />
  28.   <div class="form-group">
  29.   <label asp-for="Project.ProjectName" class="control-label"></label>
  30.   <input asp-for="Project.ProjectName" class="form-control">
  31.   </div>
  32.   <div class="form-group">
  33.   <label asp-for="Project.ProjectStateCode" class="control-label"></label>
  34.   <select asp-for="Project.ProjectStateCode" class="form-control"
  35.    
  36.   asp-items="ViewBag.ProjectStateCode">
  37.   </select>
  38.   </div>
  39.   <div class="form-group">
  40.   <input type="submit" value="Save" class="btn btn-default" />
  41.   </div>
  42.   </form>
  43.   </div>
  44.   </div>
  45.    
  46.   <div>
  47.   <a asp-page="CustomerProjects" asp-route-id="@Model.Customer.CustomerId">
  48.   Back to Customer Projects
  49.   </a>
  50.   </div>
  51.    
  52.   @section Scripts {
  53.   @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
  54.   }

关于CustomerId的隐藏<input>和<select>,此页面具有与CustomerProjectCreate页面相同的元素。

Customer Projects 页面包含Mirarex Oil&Gas的2个项目——用于编辑:

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

Mirarex Oil&Gas, Zolar Pipeline的客户项目编辑页面:

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

客户项目页面包含Mirarex Oil & Gas 的2个项目——项目编辑:

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

通过CustomerProjectDelete页面,此项目的最后一个功能是删除。

搭建 Customers/CustomerProjectDelete Razor页面:

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

初始化~Pages\Customers\CustomerProjectDelete.cshtml.cs

  1.   using System.Threading.Tasks;
  2.   using Microsoft.AspNetCore.Mvc;
  3.   using Microsoft.AspNetCore.Mvc.RazorPages;
  4.   using Microsoft.EntityFrameworkCore;
  5.   using QuantumWeb.Data;
  6.   using QuantumWeb.Model;
  7.    
  8.   namespace QuantumWeb.Pages.Customers
  9.   {
  10.   public class CustomerProjectDeleteModel : PageModel
  11.   {
  12.   private readonly QuantumDbContext _context;
  13.    
  14.   public CustomerProjectDeleteModel(QuantumDbContext context)
  15.   {
  16.   _context = context;
  17.   } // end public CustomerProjectDeleteModel(QuantumContext context)
  18.    
  19.   [BindProperty]
  20.   public Customer Customer { get; set; }
  21.   [BindProperty]
  22.   public Project Project { get; set; }
  23.    
  24.   public async Task<IActionResult> OnGetAsync(int? id)
  25.   {
  26.   if (id == null)
  27.   {
  28.   return NotFound();
  29.   } // endif (id == null)
  30.    
  31.   Project = await _context.Projects
  32.   .Include(p => p.Customer)
  33.   .FirstOrDefaultAsync(p => p.ProjectId == id);
  34.    
  35.   if (Project == null)
  36.   {
  37.   return NotFound();
  38.   } // endif (Project == null)
  39.    
  40.   Customer = Project.Customer;
  41.    
  42.   return Page();
  43.   } // end public async Task<IActionResult> OnGet(int? id)
  44.    
  45.   public async Task<IActionResult> OnPostAsync(int? id)
  46.   {
  47.   if (id == null)
  48.   {
  49.   return NotFound();
  50.   } // endif (id == null)
  51.    
  52.   Project = await _context.Projects
  53.   .Include(p => p.Customer)
  54.   .FirstOrDefaultAsync(p => p.ProjectId == id);
  55.    
  56.   if (Project != null)
  57.   {
  58.   _context.Projects.Remove(Project);
  59.   await _context.SaveChangesAsync();
  60.   } // endif (Project != null)
  61.    
  62.   return RedirectToPage("./CustomerProjects", new { id = Project.Customer.CustomerId });
  63.   } // end public async Task<IActionResult> OnPostAsync(int? id)
  64.    
  65.   } // end public class CustomerProjectDeleteModel : PageModel
  66.    
  67.   } // end namespace QuantumWeb.Pages.Customer

初始化~Pages\Customers\CustomerProjectDelete.cshtml

  1.   @page "{id:int?}"
  2.   @model QuantumWeb.Pages.Customers.CustomerProjectDeleteModel
  3.   @{
  4.   ViewData["Title"] = "Delete Customer Project";
  5.   }
  6.    
  7.   <h2>Delete Customer Project</h2>
  8.    
  9.   <h3>Are you sure you want to delete this?</h3>
  10.   <div>
  11.   <dl class="dl-horizontal">
  12.   <dt>
  13.   @Html.DisplayNameFor(model => model.Customer.CustomerName)
  14.   </dt>
  15.   <dd>
  16.   @Html.DisplayFor(model => model.Customer.CustomerName)
  17.   </dd>
  18.   <dt>
  19.   @Html.DisplayNameFor(model => model.Project.ProjectId)
  20.   </dt>
  21.   <dd>
  22.   @Html.DisplayFor(model => model.Project.ProjectId)
  23.   </dd>
  24.   <dt>
  25.   @Html.DisplayNameFor(model => model.Project.ProjectName)
  26.   </dt>
  27.   <dd>
  28.   @Html.DisplayFor(model => model.Project.ProjectName)
  29.   </dd>
  30.   <dt>
  31.   @Html.DisplayNameFor(model => model.Project.ProjectStateCode)
  32.   </dt>
  33.   <dd>
  34.   @Html.DisplayFor(model => model.Project.ProjectStateCode)
  35.   </dd>
  36.   </dl>
  37.    
  38.   <form method="post">
  39.   <input type="hidden" asp-for="Project.ProjectId" />
  40.   <a asp-page="CustomerProjects" asp-route-id="@Model.Customer.CustomerId">
  41.   Back to Customer Projects
  42.   </a> |
  43.   <input type="submit" value="Delete" class="btn btn-default" />
  44.   </form>
  45.   </div>

客户项目页面包含Mirarex Oil & Gas的3个项目:

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

删除客户项目页面——删除Ouachita Shale:

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

客户项目页面包含Mirarex Oil&Gas的2个项目:

使用EntityFramework Core和Enums作为字符串的ASP.NET Core Razor页面——第三部分

此时,我们可以总结下表中的测试数据:

Customers, Projects, ProjectStates

CustomerId

Customer Name

ProjectId

Project Name

ProjectStateCode

StateDescription

1

Mirarex Oil & Gas, LLC

1

Zolar Pipeline

UnderReview

Project is under review and negotiation

1

Mirarex Oil & Gas, LLC

2

Nelar Ranch Gas Fracturing

Prospect

Prospective or referred project

2

Polyolefin Processing, Inc.

3

Port Gibson Plant Expansion

Prospect

Prospective or referred project

2

Polyolefin Processing, Inc.

4

Jackson Plant Control System Upgrade

Prospect

Prospective or referred project

2

Polyolefin Processing, Inc.

5

Eutaw Plant Shutdown & Maintenance

Prospect

Prospective or referred project

下面可以进入第四部分进行学习。 

 https://blog.csdn.net/mzl87/article/details/85312583

原文地址:https://www.codeproject.com/Articles/1264330/ASP-NET-Core-Razor-Pages-Using-EntityFramework-C-2

上一篇:[Asp.Net Core 3.1学习笔记] Razor视图实时刷新


下一篇:Blazor 事件处理(3)