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

Entity Framework 7 – Database First – 使用 CLI

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2023年5月25日

CPOL

6分钟阅读

viewsIcon

5081

downloadIcon

65

关于使用命令行 (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. 参考文献

10. 历史

  • 2023年5月25日:初始版本
© . All rights reserved.