Entity Framework 7 – Database First – 使用 CLI
关于使用命令行 (CLI) 的 EF7 – Database First 方法的教程
↵
1. 引言
Entity Framework Core 倾向于“代码优先”方法,而有点忽视“数据库优先”方法,这导致(截至 2023 年 5 月)Visual Studio 中“数据库优先”的 GUI 界面没有正式实现。官方建议用户使用命令行(CLI)来“逆向工程”数据库 schema 到 C# Entity 类。与 GUI 使用相比,命令行命令总是难以记住且不直观。我肯定会称之为倒退,与 EF6(来自 .NET 4.8 Framework)可用的出色 GUI 工具相比。本文旨在概述“逆向工程”数据库并创建 EF7 实体类以用于严肃应用程序的实际步骤。
1.1.“数据库优先”方法的重要性
许多信息系统,特别是银行系统,都是“数据中心”的,其中数据库扮演着核心角色。应用程序围绕数据库组织,数据库是这些组织中的宇宙中心。数据库的更改通常由独立的数据库分析师或业务分析师完成,应用程序需要应对数据库 schema 的更改,并使其数据访问层(DAL)适应其他参与者对数据库所做的更改。通常,有几个应用程序正在使用,有些是遗留应用程序,使用 ADO.NET 技术或类似技术。在这种情况下谈论“代码优先”方法是不现实的。唯一可能的方法是“数据库优先”方法,适用于使用 EF/.NET 的应用程序,而且围绕数据库的其他应用程序也需要应用自己的类似“数据库优先”步骤,例如为 ADO.NET 重新创建模型等。因此,微软 Entity Framework 团队如此粗鲁地忽视 EF7 Core 中的“数据库优先”方法并将其推到命令行(CLI)是一个巨大的失望。
1.2. EFCorePowerTools
如果没有提及 EFCorePowerTools
[3],本文就不完整,它是一个用于实现 EF7 Core 的“数据库优先”方法的 GUI 工具。我不太清楚,它看起来不是一个“官方”的微软工具,而是一个“社区开源”工具。在我看来,它是一个高度可用的工具,但本文不讨论它。在这里,我们专注于“官方”命令行界面(CLI)方法。您可以在我的文章 [6] 中找到 EFCorePowerTools
的描述。
2. 示例项目
2.1. 示例控制台 .NET7 应用程序
我们创建了一个我们将使用的示例 Console .NET7 应用程序。请使用 NuGet 包管理器向应用程序添加以下包(依赖项),如屏幕截图所示
Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.SqlServer
2.2. 示例 SqlServer 数据库
我们将使用一个示例 SqlServer 数据库 Northwind
。由于数据库对象很多,我们只关注 1) 表“Customers
”、2) 视图“Invoices
”、3) 存储过程“CustOrdersOrders
”。它们在屏幕截图中进行了概述。我们将检查这些对象的“逆向工程”情况。
3. 安装 CLI EF Core 工具
您需要安装 CLI EF Core 工具并导航到*项目*文件夹。以下是您需要的命令。
dotnet tool uninstall --global dotnet-ef
dotnet tool install --global dotnet-ef --version 7.0.5
cd C:\Tmp\Example1\Example1\
以下是截图
4. 逆向工程实体(Scaffolding)
4.1. 数据库优先 – 创建模型
现在是时候做实际工作了。您需要一个类似以下的命令。我喜欢我的实体名称尽可能地与表名称相似,这就是使用一些标志的原因。另外,我不希望数据库连接字符串嵌入到代码中,我希望使用配置文件并从那里加载它。此外,我希望我的模型在一个名为 *NorthwindDB* 的单独文件夹中。
以下是逆向工程(Scaffolding)实体的命令。
//Here is the command I used (should be a one line command)
//===============================================
dotnet ef dbcontext scaffold
"Data Source=.;User Id=sa;Password=dbadmin1!;Initial Catalog=Northwind;Encrypt=False"
Microsoft.EntityFrameworkCore.SqlServer
--use-database-names
--output-dir NorthwindDB --context-dir NorthwindDB
--no-pluralize --no-onconfiguring --force
//===============================================
//Here is a legend
ef dbcontext scaffold = command name
"Data Source=.;User Id=sa;Password=dbadmin1!;Initial Catalog=Northwind;Encrypt=False"
= connection string to database
--use-database-names = I want entity names same/similar to db-object names
--output-dir NorthwindDB = output directory in my project for entities
--context-dir NorthwindDB = output directory in my project for context
--no-pluralize = I want entity names same/similar to db-object names
--no-onconfiguring = no OnCongiguring method with connect string
--force = will override any existing model
这是执行的屏幕截图
这是我们应用程序中生成的模型
请注意,已为 1) 表“Customers
”、2) 视图“Invoices
”创建了实体,但未为 3) 存储过程“CustOrdersOrders
”创建实体。此外,还生成了数据库上下文文件 *NorthwindContext*。
4.2. 不支持存储过程
令人遗憾的是,EF7 Core 的 CLI EF 工具在“数据库优先”方法中完全不支持存储过程。这是一个巨大的失望,因为 EF6 设计 GUI 工具(来自 .NET 4.8 Framework)支持存储过程。
例如,在我的 SqlServer 生产系统中,我有大约 300 个存储过程,其他团队的人有时会更改一些存储过程,如果工具不能帮助我处理这些更改,我需要手动检测它们,或者等到报告错误时才能得知有更改发生。
好消息是前面提到的 EFCorePowerTools
[3] 支持存储过程,但我们遇到了一个奇怪的情况,“官方”工具不支持它,“社区开源”支持它,等等。此外,我还在一些论坛上读到过关于 EFCorePowerTools
在存储过程支持方面的一些限制。
尽管如此,您仍然可以在 EF7“数据库优先”方法中使用存储过程,如 [4] 所述,只是您需要手动创建实体类。
4.3 创建模型的替代命令——偏爱属性
如果您更喜欢使用属性(而不是默认的首选流利 API)来配置实体类,这里是逆向工程(Scaffolding)实体的替代命令
//Here is command I used (should be one line command)
//===============================================
dotnet ef dbcontext scaffold
"Data Source=.;User Id=sa;Password=dbadmin1!;Initial Catalog=Northwind;Encrypt=False"
Microsoft.EntityFrameworkCore.SqlServer
--use-database-names
--output-dir NorthwindDB --context-dir NorthwindDB
--no-pluralize --data-annotations --no-onconfiguring --force
//===============================================
//Here is a legend
ef dbcontext scaffold = command name
"Data Source=.;User Id=sa;Password=dbadmin1!;Initial Catalog=Northwind;Encrypt=False"
= commection string to database
--use-database-names = I want entity names same/similar to db-object names
--output-dir NorthwindDB = output directory in my project for entities
--context-dir NorthwindDB = output directory in my project for context
--no-pluralize = I want entity names same/similar to db-object names
--data-annotations = Use attributes to configure the model (where possible).
If omitted, only the Fluent API is used.
--no-onconfiguring = no OnCongiguring method with connect string
--force = will override any existing model
5. 读取配置文件
为了使应用程序更专业,我们将数据库连接字符串放入配置文件 *appsettings.json* 中,并创建工厂方法来创建 DbContext
。我们不想修改 NorthwindContext
类,因为如果重新生成 EF 模型,更改将会丢失。
{
"ConnectionStrings": {
"NorthwindConnection": "Data Source=.;User Id=sa;
Password=dbadmin1!;Initial Catalog=Northwind;Encrypt=False",
}
}
您需要从 NuGet 包管理器安装更多包
internal class NorthwindContextFactory :
IDesignTimeDbContextFactory<NorthwindContext>
{
static NorthwindContextFactory()
{
IConfiguration config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", true, true)
.Build();
connectionString = config["ConnectionStrings:NorthwindConnection"];
Console.WriteLine("ConnectionString:"+connectionString);
}
static string? connectionString = null;
public NorthwindContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<NorthwindContext>();
optionsBuilder.UseSqlServer(connectionString);
return new NorthwindContext(optionsBuilder.Options);
}
}
这是现在应用程序的外观
6. 测试应用程序
我们将创建一些代码来测试我们通过 EF 生成的模型。这是测试代码
Console.WriteLine("Hello, from Example1");
using NorthwindContext ctx =
new NorthwindContextFactory().CreateDbContext(new string[0]);
Console.WriteLine("Table Customers ==================================");
var tableCustomers = ctx.Customers.Where(p => p.Country == "Germany");
foreach (var customer in tableCustomers)
{
Console.WriteLine("Customer Name: " + customer.ContactName);
}
Console.WriteLine("View Invoices ==================================");
var viewInvoices = ctx.Invoices.Where(p => p.ShipCity == "Graz");
foreach (var invoice in viewInvoices)
{
Console.WriteLine("ShipName: " + invoice.ShipName);
}
这是执行结果
7. 数据库更改
那么,如果您更改数据库 schema 会发生什么?您需要重新生成 EF 模型,上述命令将因为“–force
”标志而起作用,该标志将用新模型覆盖旧模型。这就是您不能将任何代码放入生成类中的原因,因为新的 EF 模型生成将擦除您的更改。但是,您可以并且需要利用这些类被创建为 partial
的事实,这样您就可以使用自定义 partial
类扩展生成的类。
8. 结论
我们展示了如何使用 CLI EF Core 工具从命令行 (CLI) 生成“数据库优先”EF7 模型类。这个过程并不困难,尽管不如我们在 .NET 4.8 Framework 中 EF6 中拥有的 GUI 方法那么直观。
阅读 [5] 中微软 .NET 数据和 Entity Framework 的工程经理 Arthur Vickers 的评论很有趣,他说
- “模型浏览器等可视化工具——这是我们没有计划实现的东西”
- “…可视化工具(尤其是 Visual Studio 中的那些)构建和维护成本都非常高… 不确定投资回报是否值得…”
所以,微软不会推出 Visual EF 工具,这对许多 .NET 开发者来说是个坏消息。
一个严重的问题是缺少对存储过程的支持。这几乎令人质疑这些 EF7 Core 工具在实际使用中的成熟度。甚至在某些情况下,它严重质疑 EF7 Core 在某些情况下的可用性。
我计划的下一篇文章是关于 EFCorePowerTools
的。
9. 参考文献
- [1] Entity Framework Core 工具参考
- [2] Scaffolding (逆向工程)
- [3] https://github.com/ErikEJ/EFCorePowerTools/
- [4] 在 Entity Framework Core 中使用存储过程
- [5] https://github.com/dotnet/efcore/issues/15105
- [6] Entity Framework 7 – 数据库优先 – 使用 EFCorePowerTools
10. 历史
- 2023年5月25日:初始版本