目录
介绍
背景
开发环境
使用代码
开始构建UI和数据访问
EF迁移和数据库创建
摘要
创建初始对象模型和数据库并显示第一个Razor页面
- 下载源码(QuantumWeb)
这是一篇由多部分组成的文章的第一部分,演示了通过EntityFramework Core 2.1(EF)将C#enum值映射到数据库表中的string值。它解决了enum与应用程序实体的一对多和多对多关系中的值映射问题。它在ASP.NET Core Razor Page应用程序的上下文中执行此操作。
EF是对象关系映射器(ORM)。在诸如此示例的应用程序中,有两个“世界”。一个是在C#中作为对象模型存在的对象世界。另一个是存在于关系数据库中的关系世界,如Microsoft SQL Server。这两个世界并不一致。ORM的功能,如EntityFramework,是这两个世界的桥梁,并促进它们之间的数据传输。
在第一部分中,我们将回顾以下内容:
- 示例应用程序的概念和工作对象模型
- 创建包含ASP.NET Core Razor Page项目的Visual Studio 2017(VS2017)解决方案。
- C#中的应用程序对象模型的初始创建,如Customer类。
- 设置应用程序用户界面(UI)布局和主页。
- 通过QuantumDbContext类用EF初始化客户CRUD(Create- Read- Update- Delete)页面。这包括在SQL Server实例(localdb)中创建应用程序数据库。
- 引入对象模型类的配置,以便在单独的类中与EF交互,以便于设计数据库表。
- 实现Customers/Index 页面。
枚举是由一组命名常量组成的数据类型。常量的名称往往是助记符。助记符被定义为辅助记忆的设备。但是,在枚举中存在使用助记符作为常量名称的趋势。作为开发人员,我使用的名称可能对用户来说是神秘的。从命令行界面迁移到图形用户界面(GUI)的动机的一部分源于使用助记符作为命令。此外,enum值中的默认映射是整数。整数值的含义违背了助记符的概念。
使用enum有一些优点。第一个是命名常量集,并且相对较小,便于自我验证。如果我们尝试使用未在enum中定义的值,则会生成错误。
这里实现的示例应用程序是针对一家虚拟工程技术公司,量子工程技术公司。这家公司主要服务于石油、天然气和化学工业。关键实体是Customer。这家公司服务于众多Customers。每个Customer都有多个Projects。每个Project都有一个在enum中定义的ProjectState状态。在ProjectState和Projects之间存在一个一对多的关系。该ProjectState enum允许定义状态机来协助管理Project工作流程。项目状态机超出了本文的范围。
一个项目需要一些Skills。该应用定义了一个Skill enum。在Projects和Skills之间存在多对多的关系。这些关系如下图所示,表示C#中的概念对象模型。
概念对象模型
枚举基本上包含静态数据。这些值是助记符,特别是在Skill enum中。大多数用户不容易认识到“ CtrlE ”是控制工程师而“ DataSci ”是数据科学家。这里的一个目标是实现一种方案,该方案以用户可以更容易理解的方式在用户界面(UI)中呈现这些值。为此,我们使用工作对象模型,如下所示。
工作对象模型
ProjectStateDescription实体将enum值从ProjectState映射到描述Project状态的Description 字符串。SkillTitle实体将Skill的enum值映射到SkillTitle中的Title 字符串。ProjectSkill
实体表达了Project
和Skill之间的多对多关系。我们稍后将讨论该对象模型的重要性。
示例解决方案是在以下环境中创建的:
- Visual Studio 2017(VS2017)
- ASP.NET Core 2.1
- Entity Framework Core v.2.1.4
- NuGet Package Manager v.4.6.0
- SQL Server LocalDb
- SQL Server Management Studio
我们首先创建一个空的VS2017解决方案QuantumEngSolution。有一个创建项目的选项,但无论如何都会创建一个解决方案。依我的个人风格,我更喜欢明确地创建和命名解决方案。
创建QuantumEngSolution,一个空Vs2017解决方案
单击“ 确定 ”以继续。接下来,添加一个ASP.NET Core 2.1 Web应用程序QuantumWeb。
将QuantumWeb ASP.NET Core Web应用程序添加到QuantumEngSolution
将Project配置为ASP.NET Core 2.1 Web应用程序
这将配置应用程序而无需身份验证并使用HTTPS。如果在调试和测试中遇到问题,则需要在VS2017安装中启用HTTPS。(见此链接。)
单击“ 确定 ”后,初始解决方案资源管理器如下所示:
初始解决方案资源管理器
接下来,我们向QuantumWeb项目添加一个新文件夹Model。该文件夹将包含对象模型中的文件。对于更大的应用程序,我们将创建一个类库项目来保存对象模型。这样做与关注点分离的实践是一致的,并将有助于在多个应用程序中使用对象模型。但是,由于这是一个示例演示,我们将对象模型保留在同一个项目中。
现在,我们将从类开始在Model文件夹中创建模型Customer类。
创建~QuantumWeb \ Model \ Customer.cs
初始Customer类别
namespace QuantumWeb.Model
{
///
/// Customer Class
///
public class Customer
{
#region Constructors
///
/// Parameter-less Constructor
///
///
/// Required for scaffolding the UI
///
public Customer()
{
} // end public Customer()
#endregion // Constructors
///
/// Customer Identifier, primary key
///
public int CustomerId { get; set; }
///
/// Customer Name
///
public string CustomerName { get; set; }
///
/// Primary Customer Contact
///
public string CustomerContact { get; set; }
///
/// Customer Contact Phone Number
///
public string CustomerPhone { get; set; }
///
/// Customer Contact Email Address
///
public string CustomerEmail { get; set; }
} // end public class Customer
} // end namespace QuantumWeb.Model
开始构建UI和数据访问
现在我们已经开始了对象模型,我们可以开始构建用户界面(UI)和数据访问功能。可以将ASP.NET Razor Pages配置为使用布局页面。解决方案资源管理器中的主要布局页面如下所示。
编辑主布局页面
初始化_Layout.cshtml
@ViewData["Title"] - Quantum Application
Toggle navigation
Quantum Web Application
- Home
- About
- Contact
-
Customers
@RenderBody()
© 2018 - Quantum Engineering & Technologies
@RenderSection("Scripts", required: false)
接下来,我们创建CustomersRazor页面并设置数据访问。VS2017提供了一种将CustomersUI的搭建和设置与数据访问的初始化结合起来的方法。右键单击Pages| Customers文件夹,然后在弹出菜单中选择“ 添加 ”,然后在下一个弹出窗口中选择“ 新搭建基架项目... ”。
搭建Customer Razer页面
将显示Razor 页面搭建选项对话框。选择“ 使用实体框架(CRUD)的Razer页面 ”选项。
选择“使用实体框架(CRUD)的Razer页面”
这通过EF 将一组Razor页面配置为数据库中的Create,Read,Update和Delete(CRUD)客户。
配置客户Razor页面——创建QuantumDbContext
单击每个对话框上的“ 添加 ”以构建解决方案并配置Razor页面和EF。该_Viewstart.cshtml文件指定使用_Layout.cshtml作为默认的布局。
_Viewstart.cshtml
@{
Layout = "_Layout";
}
使用CRUD页面选项的基架在项目中产生了许多变化。
搭建客户页面后的解决方案资源管理器
我们首先讨论修改和创建的文件。生成的代码存在一些问题,我们将在稍后讨论。
修改了appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"QuantumDbContext": "Server=(localdb)\\mssqllocaldb;
Database=QuantumDbContext-268e3954-a42e-460a-97a2-cff0a2cb9dd3;
Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
搭建过程为应用程序设置添加了“ ConnectionStrings”属性。这将显示一个数据库,QuantumDbContext-268e3954-a42e-460a-97a2-cff0a2cb9dd3将在SQL Server实例(localdb)上创建。基架从QuantumDbContext指定的数据上下文的名称生成默认名称。该数据库将用于初始开发和测试。它不适合生产。(译者注:数据库名称可去掉后面的数字或重命名,并在SQL数据库中创建对应名称的数据库-空的库,后面迁移用)
修改后的Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using QuantumWeb.Models;
namespace QuantumWeb
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a
// given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// The application startup specifies SQL Server and the database specified in the
// appsettings.json file
services.AddDbContext(options =>
options.UseSqlServer(Configuration.GetConnectionString("QuantumDbContext")));
}
// This method gets called by the runtime. Use this method to configure the HTTP request
// pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc();
}
}
}
Startup类包含一个应用程序启动执行的代码。其详细信息超出了本文的范围。可以在此处的 Microsoft文档中找到详细的讨论。在这里,在appsetting.json文件启动引用ConnectionString,并告诉实体框架如何找到数据库。
生成的Pages \ Index.cshtml
@page
@model IndexModel
@{
ViewData["Title"] = "Quantum Engineering & Technologies";
}
Quantum Engineering & Technologies
This is a sample application for a fictitious engineering and technology company.
Application illustrates
- Sample pages using ASP.NET Core Razor Pages
- Entity Framework Core (v. 2.1.4 used here)
-
Use of C# enumerations with members expressed in database as
strings as opposed to numbers
- One-to-many and many-to-many mapping between entities and enumerations
- Use of the Fluent API and IEntityTypeConfiguration classes for configuration
-
Theming using Bootstrap
我们用上面的HTML替换了Pages| Index页面。它提供了有关示例演示的一些信息。关键项是第一行中的“ @page” string。这是Razor页面的细节。
生成Pages \ Index.cshtml.cs
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace QuantumWeb.Pages
{
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
}
每个Razor页面由一个的.cshtml文件组成,该文件定义了在布局和呈现的UI和它的 .cshtml.cs文件,它包含对HTTP命令的处理的C#程序,主要是GET和POST。该.cshtml.cs文件是类似于WebForm的代码隐藏。但是,Razor页面的行为更像是模型——视图——控制器(MVC)模式。您可以在此处阅读有关将Razor页面与MVC进行比较的更多信息。
生成的QuantumDbContext类
using Microsoft.EntityFrameworkCore;
using QuantumWeb.Model;
namespace QuantumWeb.Models // Error!!!
{
public class QuantumDbContext : DbContext
{
public QuantumDbContext (DbContextOptions options)
: base(options)
{
} // end public QuantumDbContext (DbContextOptions options)
#region DbSets
public DbSet Customer { get; set; }
#endregion // DbSets
} // end public class QuantumDbContext : DbContext
} // end namespace QuantumWeb.Models
这显示了在搭建过程中生成的初始数据上下文类QuantumDbContext,其中添加了一些注释,以帮助提高可读性,并避免在文件稍后变得更复杂时编辑错误。另外,请注意命名空间的“Error!!!”注释。我们稍后会解决这个问题。在EF Core中,我们需要有一个派生自Microsoft.EntityFrameworkCore.DbContext的类。
使用以前的版本EntityFramework,可以在类库中轻松完成。在我们继续时,我们将使用EntityFramework迁移来创建和更新数据库。在撰写本文时,EntityFramework在类库中使用Core迁移存在问题。如果我们要轻松地将数据访问功能分离到单独的项目中并使用迁移,则最好使用数据访问服务来完成。现在,我们将放弃这一选择。
该DbContext基类包含促进与数据库的交互以及关系模型和对象模型之间关联的转换的方法。当我们继续开发时,我们将添加到这个类。
由于该数据上下文当前处于状态,因此使用默认转换将对象Customer的属性转换为数据库列。在大多数情况下,默认转换不符合应用程序的要求。为了解决这种情况,EF定义了两种技术来定义所需的转换,数据注释(此处未使用)和Fluent API。数据注释使用模型类中的属性来定义转换。数据注释有两个问题。首先,它们是有限的,不能指定Fluent API中可用的转换。其次,它们违反了对象或域模型类与数据访问之间的关注点分离原则。Fluent API在DbContext类中使用OnModelCreating方法以用于执行转换。可以通过此方法中的lambda表达式定义转换。但是,我们也可以为模型实体定义转换类,并在OnModelCreating方法中引用这些类,如下所示。
创建~\Data\CustomerConfiguration.cs
初始Data\uCustomerConfiguration.cs
[ 注意:命名空间是QuantumWeb.Data。(见下文。)]
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using QuantumWeb.Model;
namespace QuantumWeb.Data
{
public class CustomerConfiguration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
builder.ToTable("Customers");
builder.HasKey(c => c.CustomerId);
builder.Property(c => c.CustomerId)
.HasColumnType("int");
builder.Property(c => c.CustomerName)
.IsRequired()
.HasColumnType("nvarchar(50)")
.HasMaxLength(50);
builder.Property(c => c.CustomerContact)
.IsRequired()
.HasColumnType("nvarchar(50)")
.HasMaxLength(50);
builder.Property(c => c.CustomerPhone)
.IsRequired()
.HasColumnType("nvarchar(15)")
.HasMaxLength(15);
builder.Property(c => c.CustomerEmail)
.IsRequired()
.HasColumnType("nvarchar(50)")
.HasMaxLength(50);
} // end public void Configure(EntityTypeBuilder builder)
} // end public class CustomerConfiguration : IEntityTypeConfiguration
} // end namespace QuantumWeb.Data
Customer类和数据库之间的映射如下表所示:
客户类映射
项目
名称
C#类型
数据库类型
是必须的
评论
Table
Customers
CustomerId
int
int
是
Primary Key
CustomerName
string
nvarchar(50)
是
CustomerContact
string
nvarchar(50)
是
CustomerPhone
string
nvarchar(15)
是
CustomerEmail
string
nvarchar(50)
是
Data\QuantumDbContext.cs的第一次更新
[ 注意:命名空间是QuantumWeb.Data。(见下文。)]
using Microsoft.EntityFrameworkCore;
using QuantumWeb.Model;
namespace QuantumWeb.Data
{
public class QuantumDbContext : DbContext
{
public QuantumDbContext (DbContextOptions options)
: base(options)
{
} // end public QuantumDbContext (DbContextOptions options)
#region DbSets
public DbSet Customer { get; set; }
#endregion // DbSets
///
/// Data Model Creation Method
///
/// ModelBuilder instance
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new CustomerConfiguration());
} // end protected override void OnModelCreating(ModelBuilder modelBuilder)
} // end public class QuantumDbContext : DbContext
} // end namespace QuantumWeb.Data
该modelBuilder.ApplyConfiguration()调用将一个CustomerConfiguration实例注入数据转换逻辑。
生成的Pages\Customers\Index.cshtml
@page
@model QuantumWeb.Pages.Customers.IndexModel
@{
ViewData["Title"] = "Index";
}
Index
Create New
@Html.DisplayNameFor(model => model.Customer[0].CustomerName)
@Html.DisplayNameFor(model => model.Customer[0].CustomerContact)
@Html.DisplayNameFor(model => model.Customer[0].CustomerPhone)
@Html.DisplayNameFor(model => model.Customer[0].CustomerEmail)
@foreach (var item in Model.Customer) {
@Html.DisplayFor(modelItem => item.CustomerName)
@Html.DisplayFor(modelItem => item.CustomerContact)
@Html.DisplayFor(modelItem => item.CustomerPhone)
@Html.DisplayFor(modelItem => item.CustomerEmail)
Edit |
Details |
Delete
}
在Pages/Customers/Index.cshtml页面中列出现有的Customers,并具有创建新客户以及编辑、显示细节和删除Customer数据库中的记录的链接。
生成Pages\Customers\Index.cshtml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
// No reference to QuantumWeb.Data namespace. Error!!!
using QuantumWeb.Model;
using QuantumWeb.Models; // Error!!!
namespace QuantumWeb.Pages.Customers
{
public class IndexModel : PageModel
{
private readonly QuantumWeb.Models.QuantumDbContext _context; // Error!!!
public CreateModel(QuantumWeb.Models.QuantumDbContext context) // Error!!!
{
_context = context;
}
public IList Customer { get; set; }
public async Task OnGetAsync()
{
Customer = await _context.Customer.ToListAsync();
}
}
}
这是Customers / Index页面的处理程序。注意标有注释“// Error!!”的行。这反映了搭建过程之后的上述问题。
- 请注意该行,使用QuantumWeb.Models。此命名空间不存在。模型的正确命名空间是QuantumWeb.Model。应从已生成的所有.cshtml.cs文件中删除此行。
- QuantumDbContext类是引用自QuantumWeb.Models命名空间。该文件位于QuantumWeb \ Data文件夹中。我们将此命名空间更改为QuantumWeb.Data。原因是实现关注点分离的最佳实践,以将数据访问实体,如QuantumDbContext,与对象模型实体分开。为了实现这个概念,我们在使用QuantumDbContext的所有.cshtml.cs文件中添加了对QuantumWeb.Data命名空间的引用。请注意上面第一次更新到Data\QuantumDbContext.cs和初始化Data\CustomerConfiguration.cs中的命名空间。
对Pages\Customers\Index.cshtml.cs的第一次修改
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using QuantumWeb.Data;
using QuantumWeb.Model;
namespace QuantumWeb.Pages.Customers
{
public class IndexModel : PageModel
{
private readonly QuantumDbContext _context;
public IndexModel(QuantumDbContext context)
{
_context = context;
} // end public IndexModel(QuantumDbContext context)
public IList Customer { get;set; }
public async Task OnGetAsync()
{
Customer = await _context.Customer.ToListAsync();
} // end public async Task OnGetAsync()
} // end public class IndexModel : PageModel
} // end namespace QuantumWeb.Pages.Customers
我们对Pages\Customers\ .cshtml.cs文件进行了类似的更改。
EF迁移和数据库创建此时,我们可以解决EF迁移并创建我们的演示数据库。首先,我们需要安装一个NuGet包Microsoft.EntityFrameworkCore.Tools v.2.1.4。该软件包允许我们在程序包管理器控制台中使用某些命令。通常,程序包管理器控制台在VS2017 IDE中作为“输出窗口”中的选项卡显示。
VS2017 IDE中的程序包管理器控制台
如果看不到程序包管理器控制台,则可以使用“ 工具”>“NuGet程序包管理器”>“程序包管理器控制台”菜单命令将其打开。
在VS2017 IDE中打开程序包管理控制台
这里完成的大部分工作都使用程序包管理控制台。您可以在此处浏览有关程序包管理控制台的详细信息。
有两种方法可以安装NuGet包。一种是在Package Console Manager中使用命令。
[ 警告:在具有多个项目的解决方案中,请确保在程序包管理器控制台中引用了正确的项目。]
Install-Package Microsoft.EntityFrameworkCore.Tools -Version 2.1.4
另一个是使用Solution Explorer完成的。
在VS2017 IDE中安装NuGet Package,Microsoft.EntityFrameworkCore v.2.1.4
单击“ 安装 ”以安装软件包。
现在我们可以创建初始迁移。这将使QuantumDbContext和CustomerConfiguration类中的代码生成一个Migration,以处理应用程序和连接的数据库之间的数据传输。(译者注:如果用的现有项目,因项目中已经有了Migrations文件夹,所有可以考虑删除后在执行下面的命令)
Add-Migration Initial-Migration
此命令从程序包管理器控制台执行。这将修改Solution Explorer,如图所示。
初始迁移后的Solution Explorer
生成20181019171417_Initial-Migration.cs
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
namespace QuantumWeb.Migrations
{
public partial class InitialMigration : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Customers",
columns: table => new
{
CustomerId = table.Column(type: "int", nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy",
SqlServerValueGenerationStrategy.IdentityColumn),
CustomerName = table.Column(type: "nvarchar(50)",
maxLength: 50, nullable: false),
CustomerContact = table.Column(type: "nvarchar(50)",
maxLength: 50, nullable: false),
CustomerPhone = table.Column(type: "nvarchar(15)",
maxLength: 15, nullable: false),
CustomerEmail = table.Column(type: "nvarchar(50)",
maxLength: 50, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Customers", x => x.CustomerId);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Customers");
}
}
}
Migrations类定义了两种方法,Up,它处理从应用程序到数据库转移而Down,它处理的这种转移的逆转。在进行下一步之前,最好检查生成的迁移代码。
Update-Database
同样,该Update-Database命令在程序包管理器控制台中执行。
如果这些命令成功,则更改将传播到数据库。如果这是针对数据库的第一次迁移,并且数据库不存在,则会创建数据库。这假设您有权在数据库服务器上创建数据库。我们现在可以在VS2017 Server Explorer中创建与数据库的连接。
在VS2017 Server Explorer中创建数据库连接
只需右键单击“ 数据连接 ”节点,然后选择“ 添加选择... ”。
指定服务器和选择数据库
完成此操作后,您可以单击“ 测试 ”按钮以确认数据库是否存在,以及您是否可以从VS2017连接到该数据库。
测试和创建连接
单击“ 确定 ”按钮以创建连接。连接完成后,您可以在服务器资源管理器中单击其节点打开它。然后,打开“ Tables”节点并右键单击Customers表并选择“ Open Table Definition ”。然后,您应该在数据库中看到表定义,如下所示。
QuantumDbContext数据库中的客户表定义
此时,我们可以在调试器中构建和执行应用程序。
QuantumWeb应用程序主页:https//localhost: 44306/
单击“ Customers ”链接以查看Customers页面。
QuantumWeb应用程序客户页面:https //localhost:44306/Customers
没有Customer记录,因此,我们的下一个任务是完成CustomerUI。
摘要此时,我们最初创建了一个ASP.NET Core Razor Page应用程序QuantumWeb,它显示了一个Customers Index页面。我们已经通过QuantumDbContext类完成了初始数据访问功能,并在localdb实例中创建了应用程序数据库。
下面可以进入第二部分进行学习。
原文地址:https://www.codeproject.com/Articles/1263791/ASP-NET-Core-Razor-Pages-Using-EntityFramework-Cor