管理数据库架构
EF Core提供了两种保持EF Core模型和数据库模式同步的主要方法。要在两者之间进行选择,请确定您的EF核心模型还是数据库模式是真实的来源。
1、如果您希望您的EF核心模型是真实的来源,那么使用迁移。当您对EF核心模型进行更改时,这种方法将递增地对数据库应用相应的模式更改,以便它与EF核心模型保持兼容。
2、如果您想让数据库模式成为事实的来源,请使用反向工程。这种方法允许您通过反向工程数据库模式到EF核心模型来构建DbContext和实体类型类。
create and drop APIs 还可以从您的EF核心模型创建数据库模式。但是,它们主要用于测试、原型和其他可以删除数据库的场景。
一、迁移
在现实世界的项目中,数据模型随着功能的实现而变化:添加和删除新的实体或属性,并且需要相应地更改数据库模式以保持与应用程序同步。EF Core中的迁移特性提供了一种增量更新数据库模式的方法,使其与应用程序的数据模型保持同步,同时在数据库中保留现有数据。
迁移的功能如下:
- 当引入数据模型更改时,开发人员使用 EF Core 工具添加相应的迁移,以描述使数据库架构保持同步所需的更新。EF Core 将当前模型与旧模型的快照进行比较,以确定差异,并生成迁移源文件;文件可在项目的源代码管理中进行跟踪,如任何其他源文件。
- 生成新的迁移后,可通过多种方式将其应用于数据库。 EF Core 在一个特殊的历史记录表中记录所有应用的迁移,使其知道哪些迁移已应用,哪些迁移尚未应用。
首先,必须安装 EF Core 命令行工具:
- 使用 .NET Core CLI 工具,该工具适用于所有平台。
- 如果你更喜欢在 Visual Studio 内工作或你具有 EF6 迁移经验,还可以使用程序包管理器控制台工具。
1、管理迁移
当模型发生更改时,将在正常开发过程中添加和删除迁移,并将迁移文件签入项目的源代码管理。 如果 DbContext
与启动项目位于不同程序集中,可以在包管理器控制台工具或 .NET Core CLI 工具中显式指定目标和启动项目。
1、添加迁移
更改模型后,可以为该更改添加迁移:
Add-Migration AddBlogCreatedTimestamp
迁移名称可以像版本控制系统中的提交消息一样使用。例如,如果更改是您的Blog实体上的一个新的CreatedTimestamp属性,您可能会选择一个类似AddBlogCreatedTimestamp的名称。
Migrations目录下的三个文件被添加到您的项目中:
-
- XXXXXXXXXXXXXX_AddCreatedTimestamp.cs--主迁移文件。包含应用迁移(Up)和恢复迁移(Down)所需的操作。
- XXXXXXXXXXXXXX_AddCreatedTimestamp.Designer.cs--迁移元数据文件。包含EF使用的信息。
- MyContextModelSnapshot.cs--当前模型的快照。用于确定添加下一个迁移时发生的更改。
文件名中的时间戳有助于保持它们按时间顺序排列,因此您可以看到更改的进展。
名称空间
您可以随意移动迁移文件并手动更改它们的名称空间。新迁移作为上一次迁移的兄弟迁移创建。或者,您可以在生成时指定目录,如下所示:
Add-Migration InitialCreate -OutputDir Your\Directory
在EF Core 5.0中,您还可以使用-Namespace更改独立于目录的名称空间。
2、自定义迁移代码
尽管 EF Core 通常会创建准确的迁移,但应始终查看代码,并确保其对应于所需的更改;在某些情况下,它甚至是必需的。
重命名列 --尤其要注意这点
定制迁移时,一个值得注意的例子是重命名属性时。例如,如果你将一个属性从Name重命名为FullName, EF Core将生成以下迁移:
migrationBuilder.DropColumn( name: "Name", table: "Customers"); migrationBuilder.AddColumn<string>( name: "FullName", table: "Customers", nullable: true);
EF Core 通常无法知道何时要删除某一列,并创建一个新的 (两个不同的更改) ,以及应重命名列。 如果以上迁移按原样应用,则所有客户名称都将丢失。 若要重命名列,请将上面生成的迁移替换为以下内容:
migrationBuilder.RenameColumn( name: "Name", table: "Customers", newName: "FullName");
当某个操作可能会导致数据丢失(例如删除某列),搭建迁移基架过程将对此发出警告。 如果看到此警告,务必检查迁移代码的准确性。
添加原始SQL
重命名列时,可以通过内置 API 来实现,在许多情况下,这是不可能的。 例如,我们可能希望将现有的 FirstName
和属性替换为 LastName
一个新的 FullName
属性。 EF Core 生成的迁移如下:
migrationBuilder.DropColumn( name: "FirstName", table: "Customer"); migrationBuilder.DropColumn( name: "LastName", table: "Customer"); migrationBuilder.AddColumn<string>( name: "FullName", table: "Customer", nullable: true);
如前所述,这会导致不需要的数据丢失。 为了传输旧列中的数据,我们会重新排列迁移并引入原始 SQL 操作,如下所示:
migrationBuilder.AddColumn<string>( name: "FullName", table: "Customer", nullable: true); migrationBuilder.Sql( @" UPDATE Customer SET FullName = FirstName + ' ' + LastName; "); migrationBuilder.DropColumn( name: "FirstName", table: "Customer"); migrationBuilder.DropColumn( name: "LastName", table: "Customer");
通过原始 SQL 进行任意更改
原始SQL还可以用来管理EF Core不知道的数据库对象。为了做到这一点,添加一个迁移而不做任何模型更改;将生成一个空迁移,然后您可以使用原始SQL操作填充该迁移。例如,下面的迁移创建了一个SQL Server存储过程:
migrationBuilder.Sql( @" EXEC ('CREATE PROCEDURE getFullName @LastName nvarchar(50), @FirstName nvarchar(50) AS RETURN @LastName + @FirstName;')");
当语句必须是SQL批处理中的第一个或唯一一个语句时,使用EXEC。它还可以用于解决幂等迁移脚本中的解析器错误,当前表中不存在引用的列时,这种错误可能发生。
这可用于管理数据库的任何方面,包括:
- 全文搜索
- 函数
- 触发器
- 视图
- 存储过程
在大多数情况下,当应用迁移时,EF Core会自动将每个迁移包装到它自己的事务中。不幸的是,某些数据库中的某些迁移操作不能在事务内执行;对于这些情况,您可以通过将suppressTransaction: true传递给migrationBuilder.Sql来退出事务。
如果DbContext与启动项目位于不同的程序集中,您可以在Package Manager控制台工具或. net Core CLI工具中显式地指定目标和启动项目。
3、删除迁移
有时,你可能在添加迁移后意识到需要在应用迁移前对 EF Core 模型作出其他更改。 要删除上个迁移,请使用如下命令:
Remove-Migration
删除迁移后,可对模型作出其他更改,然后再次添加迁移。
注意不要删除已经应用到生产数据库的任何迁移。如果不这样做,您将无法恢复它,并且可能会破坏后续迁移所做的假设。
3、列出迁移
列出所有已存在的迁移:
Get-Migration
这是EF Core 5的新特性。
5、重置所有迁移
在某些极端情况下,可能需要删除所有迁移并重新开始。这可以通过删除Migrations文件夹和删除数据库轻松完成(会删除数据);此时,您可以创建一个新的初始迁移,它将包含您整个当前模式。
还可以重置所有迁移并创建一个迁移,而不会丢失数据。这有时被称为“squashing”(挤压?),涉及一些手工工作:
-
- 删除 迁移 文件夹
- 创建新迁移并为其生成 SQL 脚本(如何生成见后文)
- 在数据库中,删除迁移历史记录表中的所有行
- 将单个行插入到迁移历史记录中,记录已应用的第一个迁移,因为表已经存在。 Insert SQL 是上面生成的 SQL 脚本中的最后一个操作。
删除 迁移 文件夹时,任何 自定义迁移代码都将丢失。 若要保留任何自定义,必须手动将其应用到新的初始迁移。
2、应用迁移
添加迁移后,需要对其进行部署并将其应用于数据库。 有多种策略可用于执行此操作,某些策略更适用于生产环境,而另一些则用于开发生命周期。无论部署策略如何,都应始终检查生成的迁移并在应用到生产数据库之前对其进行测试。 如果尝试对列进行重命名,则迁移可能会删除列,如果应用到数据库,则可能会因各种原因而失败。
1、SQL脚本
部署到生产数据库的建议方法是生成 SQL 脚本。 此策略的优点包括:
- 可以查看 SQL 脚本的准确性;这一点很重要,因为对生产数据库应用架构更改可能会导致数据丢失。
- 在某些情况下,可以根据生产数据库的特定需求调整这些脚本。
- SQL 脚本可以与部署技术结合使用,甚至可以作为 CI 过程的一部分生成。
- SQL 脚本可提供给 DBA,可以单独管理和存档。
基本用法
下面的内容生成一个从空白数据库到最新迁移的 SQL 脚本:
Script-Migration
使用From
以下生成从给定的迁移到最新迁移的 SQL 脚本:
Script-Migration AddNewTables
使用From和To
下面生成一个SQL脚本,从指定的 from
迁移到指定的To迁移(您可以使用比to更新的from来生成回滚脚本。请注意潜在的数据丢失情况):
Script-Migration AddNewTables AddAuditTable
脚本生成接受以下两个自变量,以指示应生成的迁移范围:
- from 迁移应是运行该脚本前应用到数据库的最后一个迁移。 如果未应用任何迁移,请指定
0
(默认值)。 - to 迁移是运行该脚本后应用到数据库的最后一个迁移。 它默认为项目中的最后一个迁移。
2、幂等SQL脚本
上面生成的 SQL 脚本只能应用于将架构从一个迁移更改到另一个迁移;你需要负责适当地应用脚本,并且仅适用于处于正确迁移状态的数据库。 EF Core 还支持生成 幂等 脚本,这些脚本可以在内部检查哪些迁移已经被应用(通过迁移历史表),并且只应用缺失的迁移。 如果您不知道最后一次应用到数据库的迁移,或者要部署到多个数据库(每个数据库都在不同的迁移上),这会很有用。
以下生成幂等迁移:
Script-Migration -Idempotent
3、命令行工具
可以使用EF命令行工具对数据库进行迁移。虽然这种方法对于本地开发和迁移测试很有效,但对于管理生产数据库并不理想:
-
- 该工具直接应用SQL命令,而不给开发人员检查或修改它们的机会。在生产环境中,这可能很危险。
- 必须在生产服务器上安装。net SDK和EF工具。
以下操作将您的数据库更新到最新的迁移:
Update-Database
下面的代码更新你的数据库到一个给定的迁移:
Update-Database AddNewTables
请注意,这种方式也可以用于回滚到以前的迁移(常记:注意潜在的数据丢失情况)。
4、运行时迁移
应用程序本身可以通过编程方式应用迁移,通常在启动期间。虽然这种方法对于本地开发和迁移测试是有效的,但它不适用于管理生产数据库,原因如下:
-
-
- 如果你的应用程序的多个实例正在运行,这两个应用程序都可以尝试同时应用迁移和失败 (或更糟,导致数据损坏) 。
- 同样,如果应用程序在另一个应用程序迁移它时访问数据库,这可能会导致严重问题。
- 应用程序必须具有提升的访问权限才能修改数据库架构。 在生产环境中限制应用程序的数据库权限通常是一种很好的做法。
- 在出现问题时,必须能够回滚应用的迁移。 其他策略可轻松地提供这种情况。
- SQL 命令直接应用于程序,没有为开发人员提供检查或修改的机会。 这在生产环境中可能会很危险。
-
要以编程方式应用迁移,可以调用context.Database.Migrate()。例如,一个典型的ASP.NET应用程序可以做到以下几点:
public static void Main(string[] args) { var host = CreateHostBuilder(args).Build(); using (var scope = host.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>(); db.Database.Migrate(); } host.Run(); }
注意,Migrate()构建在IMigrator服务之上,可以用于更高级的场景。使用myDbContext.GetInfrastructure(). getservice <IMigrator>()来访问它。
-
-
- 在生产中使用此方法之前请仔细考虑。经验表明,这种部署策略的简单性被它所产生的问题压倒了。请考虑从迁移生成SQL脚本。
- 请勿在
Migrate()
前调用EnsureCreated()
。EnsureCreated()
会绕过迁移创建架构,这会导致Migrate()
失败。
-
3、团队环境
在团队环境中使用迁移时,请特别注意模型快照文件。 此文件可以告诉你队友的迁移是否与你的迁移完全合并,或者是否需要在共享之前重新创建您的迁移来解决冲突。
1、合并
合并团队成员的迁移时,可能会在模型快照文件中出现冲突。 如果这两个更改不相关,则合并很简单,两个迁移可以共存。 例如,你可能会在客户实体类型配置中出现合并冲突,如下所示:
<<<<<<< Mine b.Property<bool>("Deactivated"); ======= b.Property<int>("LoyaltyPoints"); >>>>>>> Theirs
由于这两个属性都需要存在于最终模型中,因此请通过添加这两个属性来完成合并。 在许多情况下,版本控制系统可能会自动合并此类更改:
b.Property<bool>("Deactivated"); b.Property<int>("LoyaltyPoints");
在这些情况下,迁移和队友的迁移彼此独立。 由于可以首先应用其中的任何一个,因此在与团队共享之前,无需对迁移进行任何其他更改。
2、解决冲突
有时,在合并模型快照模型时,您会遇到真正的冲突。例如,您和您的队友可能已经分别重命名了相同的属性:
<<<<<<< Mine b.Property<string>("Username"); ======= b.Property<string>("Alias"); >>>>>>> Theirs
如果遇到这种冲突,请通过重新创建迁移解决此问题。 请执行下列步骤:
- 在合并前中止合并并回退到工作目录
- 删除迁移 (但保留模型更改)
- 将队友的更改合并到工作目录
- 重新添加迁移
执行此操作后,可以按正确的顺序应用两个迁移。 首先应用队友的迁移,将列重命名为 Alias,此后,迁移会将其重命名为 Username。
4、自定义操作
MigrationBuilder API允许您在迁移过程中执行许多不同类型的操作,但它还远远不够详尽。然而,API也是可扩展的,允许您定义自己的操作。有两种方法可以扩展API:使用Sql()方法,或者通过定义自定义的MigrationOperation对象。
为了说明这一点,让我们来看看如何实现使用每种方法创建数据库用户的操作。在我们的迁移中,我们希望能够编写以下代码:
migrationBuilder.CreateUser("SQLUser1", "Password");
1、使用MigrationBuilder.Sql()
实现自定义操作的最简单方法是定义调用的扩展方法 MigrationBuilder.Sql()
。 下面是生成相应 Transact-sql 的示例:
static OperationBuilder<SqlOperation> CreateUser( this MigrationBuilder migrationBuilder, string name, string password) => migrationBuilder.Sql($"CREATE USER {name} WITH PASSWORD '{password}';");
当语句必须是SQL批处理中的第一个或唯一一个语句时,请使用EXEC函数。它还可能需要解决幂等迁移脚本中的解析器错误,当表中当前不存在引用的列时,这种错误可能发生。
如果你的迁移需要支持多个数据库提供程序,则可以使用 MigrationBuilder.ActiveProvider
属性。 下面是同时支持 Microsoft SQL Server 和 PostgreSQL 的示例:
static OperationBuilder<SqlOperation> CreateUser( this MigrationBuilder migrationBuilder, string name, string password) { switch (migrationBuilder.ActiveProvider) { case "Npgsql.EntityFrameworkCore.PostgreSQL": return migrationBuilder .Sql($"CREATE USER {name} WITH PASSWORD '{password}';"); case "Microsoft.EntityFrameworkCore.SqlServer": return migrationBuilder .Sql($"CREATE USER {name} WITH PASSWORD = '{password}';"); } throw new Exception("Unexpected provider."); }
此方法仅在知道将应用自定义操作的每个提供程序时才有效。
2、使用自定义的MigrationOperation对象。
要将自定义操作与SQL解耦,可以定义自己的MigrationOperation来表示它。然后将操作传递给提供程序,以便它生成的适当SQL:
class CreateUserOperation : MigrationOperation { public string Name { get; set; } public string Password { get; set; } }
使用这种方法,扩展方法只需要将其中一个操作添加到MigrationBuilder.Operations:
static OperationBuilder<CreateUserOperation> CreateUser( this MigrationBuilder migrationBuilder, string name, string password) { var operation = new CreateUserOperation { Name = name, Password = password }; migrationBuilder.Operations.Add(operation); return new OperationBuilder<CreateUserOperation>(operation); }
这种方法要求每个提供程序都知道如何在其IMigrationsSqlGenerator服务中为该操作生成SQL。下面是一个覆盖SQL Server生成器以处理新操作的示例:
class MyMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator { public MyMigrationsSqlGenerator( MigrationsSqlGeneratorDependencies dependencies, IMigrationsAnnotationProvider migrationsAnnotations) : base(dependencies, migrationsAnnotations) { } protected override void Generate( MigrationOperation operation, IModel model, MigrationCommandListBuilder builder) { if (operation is CreateUserOperation createUserOperation) { Generate(createUserOperation, builder); } else { base.Generate(operation, model, builder); } } private void Generate( CreateUserOperation operation, MigrationCommandListBuilder builder) { var sqlHelper = Dependencies.SqlGenerationHelper; var stringMapping = Dependencies.TypeMappingSource.FindMapping(typeof(string)); builder .Append("CREATE USER ") .Append(sqlHelper.DelimitIdentifier(operation.Name)) .Append(" WITH PASSWORD = ") .Append(stringMapping.GenerateSqlLiteral(operation.Password)) .AppendLine(sqlHelper.StatementTerminator) .EndCommand(); } }
将默认迁移 sql 生成器服务替换为已更新的:
protected override void OnConfiguring(DbContextOptionsBuilder options) => options .UseSqlServer(_connectionString) .ReplaceService<IMigrationsSqlGenerator, MyMigrationsSqlGenerator>();
5、使用独立的项目
您可能希望将迁移存储在一个与包含DbContext的项目不同的项目中。您还可以使用此策略来维护多组迁移,例如,一组用于开发,另一组用于发布到发布的升级。
步骤:
- 创建一个新的类库。
- 添加对 DbContext 项目的引用。
- 将迁移和模型快照文件移动到类库。如果您没有现有的迁移,那么在包含DbContext的项目中生成一个迁移,然后移动它。这很重要,因为如果migrations项目不包含现有的迁移,那么Add-Migration命令将无法找到DbContext。
- 配置迁移程序集:
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection"), x => x.MigrationsAssembly("WebApplication1.Migrations")));
- 从 启动 项目添加对迁移项目的引用。
<ItemGroup> <ProjectReference Include="..\WebApplication1.Migrations\WebApplication1.Migrations.csproj"> </ItemGroup>
如果这导致循环依赖,您可以改为更新migrations项目的基本输出路径:
<PropertyGroup> <BaseOutputPath>..\WebApplication1\bin\</BaseOutputPath> </PropertyGroup>
如果一切正常,应能够向项目添加新的迁移:
Add-Migration NewMigration -Project WebApplication1.Migrations
6、多个提供程序
EF Core工具只支持活动提供程序(active provider)的迁移。然而,有时您可能希望在DbContext中使用多个提供者(例如Microsoft SQL Server和SQLite)。通过维护多个迁移集(每个提供者一个集合、组)来处理这个问题,并为每个集合添加一个迁移。
1、使用多种DbContext上下文类型
创建多个迁移集的一种方法是为每个提供程序使用一个 DbContext 类型:
class SqliteBlogContext : BlogContext { protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlite("Data Source=my.db"); }
添加新迁移时指定上下文类型:
Add-Migration InitialCreate -Context BlogContext -OutputDir Migrations\SqlServerMigrations Add-Migration InitialCreate -Context SqliteBlogContext -OutputDir Migrations\SqliteMigrations
无需指定输出目录即可进行后续迁移,因为它们是以同级方式创建的。
2、使用一个上下文类型
也可以使用一个DbContext类型。这目前需要将迁移转移到单独的程序集中。
从EF Core 5.0开始,你可以通过工具将参数传递到应用程序中。这可以使工作流程更加流畅,从而避免在运行工具时不得不对项目进行手动更改。
在使用通用主机时,有一种模式可以很好地工作:
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureServices( (hostContext, services) => { services.AddHostedService<Worker>(); // Set the active provider via configuration var configuration = hostContext.Configuration; var provider = configuration.GetValue("Provider", "SqlServer"); services.AddDbContext<BlogContext>( options => _ = provider switch { "Sqlite" => options.UseSqlite( configuration.GetConnectionString("SqliteConnection"), x => x.MigrationsAssembly("SqliteMigrations")), "SqlServer" => options.UseSqlServer( configuration.GetConnectionString("SqlServerConnection"), x => x.MigrationsAssembly("SqlServerMigrations")), _ => throw new Exception($"Unsupported provider: {provider}") }); });
由于默认主机生成器从命令行参数读取配置,因此您可以在运行工具时指定提供程序:
Add-Migration MyMigration -Args "--provider SqlServer" Add-Migration MyMigration -Args "--provider Sqlite"
7、自定义历史记录表
默认情况下,EF Core通过在一个名为__EFMigrationsHistory的表中记录它们来跟踪哪些迁移已经被应用到数据库。出于各种原因,您可能希望定制此表以更好地满足您的需求。(如果在应用迁移 后 自定义迁移历史记录表,则需要负责更新数据库中的现有表。)
1、自定义架构和表名称
你可以使用OnConfiguring()中的MigrationsHistoryTable()方法来更改模式和表名(或ASP.NET Core上的ConfigureServices())。下面是一个使用SQL Server EF核心提供程序的示例:
protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlServer( _connectionString, x => x.MigrationsHistoryTable("__MyMigrationsHistory", "mySchema"));
2、其他更改
若要配置表的其他方面,请覆盖并替换特定于提供程序的IHistoryRepository服务。下面是一个将SQL Server上的MigrationId列名称更改为Id的示例:
protected override void OnConfiguring(DbContextOptionsBuilder options) => options .UseSqlServer(_connectionString) .ReplaceService<IHistoryRepository, MyHistoryRepository>();
class MyHistoryRepository : SqlServerHistoryRepository { public MyHistoryRepository(HistoryRepositoryDependencies dependencies) : base(dependencies) { } protected override void ConfigureTable(EntityTypeBuilder<HistoryRow> history) { base.ConfigureTable(history); history.Property(h => h.MigrationId).HasColumnName("Id"); } }
SqlServerHistoryRepository
位于内部命名空间内部
二、反向工程
逆向工程是基于数据库模式搭建实体类型类和DbContext类的过程。可以使用EF核心包管理器控制台(PMC)工具的scaffold - dbcontext命令或. net命令行接口(CLI)工具的dotnet EF dbcontext scaffold命令来执行。
1、安装
在逆向工程之前,您需要安装PMC工具(仅Visual Studio)或CLI工具。
您还需要为想要反向工程的数据库模式安装适当的数据库提供程序。
2、连接字符串
该命令的第一个参数是到数据库的连接字符串。工具将使用这个连接字符串来读取数据库模式。
如何引用和转义连接字符串取决于您正在使用哪个shell来执行命令。有关详细信息,请参考您的shell文档。例如,PowerShell要求转义$字符,但不要求转义\。
Scaffold-DbContext 'Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook' Microsoft.EntityFrameworkCore.SqlServer
配置和用户密码
在ASP.NET Core项目中,你可以使用Name=<connection-string>语法从配置中读取连接字符串。
这可以很好地与Secret Manager工具一起工作,使数据库密码与代码库分离。
.Net Core CLI:
dotnet user-secrets set ConnectionStrings.Chinook "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" dotnet ef dbcontext scaffold Name=ConnectionStrings.Chinook Microsoft.EntityFrameworkCore.SqlServer
3、提供程序名称
第二个参数是提供程序名称。 提供程序名称通常与提供程序的 NuGet 包名称相同。
4、指定表
默认情况下,将数据库架构中的所有表反向工程为实体类型。 您可以通过指定架构和表来限制对哪些表进行反向工程:
-Schemas选项可用于包含架构中的每个表,而-Tables可用于包含特定的表。
要包含多个表,请使用数组:
Scaffold-DbContext ... -Tables Artist, Album
5、保留名称
默认情况下,表名和列名是固定的,以便更好地匹配.net类型和属性的命名约定。在PMC中指定-UseDatabaseNames开关或在.net Core CLI中指定——use-database-names选项将禁用此行为,尽可能保留原始的数据库名称。无效的.net标识符仍然会被修复,像导航属性这样的合成名称仍然会遵循.net命名约定。
6、使用Fluent API还是数据批注的方式
默认情况下,实体类型是使用熟知 API 配置的。 指定 -DataAnnotations
(PMC) 或 --data-annotations
( .NET Core CLI) 改为使用数据批注(如果可能)。
7、DbContext 名称
默认情况下,基架 DbContext 类名称将为带有后缀 Context 的数据库的名称。 若要指定其他项,请在 PMC 中使用-Context参数, 在 .NET Core CLI 中使用--context
参数。
8、目录和命名空间
实体类和DbContext类被搭建到项目的根目录中,并使用项目的默认名称空间。
你可以使用-OutputDir指定搭建类的目录,-ContextDir可以用来搭建DbContext类到实体类型类的单独目录中:
Scaffold-DbContext ... -ContextDir Data -OutputDir Models
默认情况下,名称空间将是根名称空间加上项目根目录下任何子目录的名称。但是,从EFCore 5.0开始,您可以使用-Namespace覆盖所有输出类的名称空间。还可以使用-ContextNamespace覆盖DbContext类的名称空间:
Scaffold-DbContext ... -Namespace Your.Namespace -ContextNamespace Your.DbContext.Namespace
9、工作原理
逆向工程从读取数据库模式开始。它读取关于表、列、约束和索引的信息。
接下来,它使用schema信息来创建EF核心模型。表用于创建实体类型;列用于创建属性;外键用于创建关系。
最后,使用该模型生成代码。它构建了相应的实体类型类、Fluent API和数据注释,用以从应用程序中重新创建相同的模型。
10、限制
-
- 不是有关模型的所有内容都可以使用数据库架构来表示。 例如,有关 继承层次结构、 附属类型和 表拆分 的信息在数据库架构中不存在。 因此,这些构造永远不会经过反向工程。
- 此外,EF Core 提供程序可能不支持 某些列类型 。 这些列不会包含在模型中。
- 可以在 EF Core 模型中定义 并发标记,以防止两个用户同时更新同一实体。 某些数据库具有特殊类型来表示此类型的列 (例如,SQL Server 中的 rowversion) ,在这种情况下,我们可以对此信息进行反向工程;但是,其他并发令牌不会进行反向工程。
- 反向工程当前不支持c # 8 可为 Null 的引用类型功能: EF Core 始终会生成假定禁用该功能的 c # 代码。 例如,可以为空的文本列将搭建为一个类型为string的属性,而不是【string?】,配置属性是否是必须的使用Fluent API 或数据批注(原文:For example, nullable text columns will be scaffolded as a property with type
string
, notstring?
, with either the Fluent API or Data Annotations used to configure whether a property is required or not. )。 您可以编辑搭建好的代码,并用c#的可空注释替换它们。
7、自定义模型
由EF Core生成的代码就是你的代码。您可以随意更改它。只有当您再次对相同的模型进行逆向工程时,它才会重新生成。搭建的代码表示一个可用于访问数据库的模型,但它肯定不是惟一可用的模型。
可以根据需要,自定义实体类型类和DbContext类。例如,您可以选择重命名类型和属性、引入继承层次结构或将表拆分为多个实体。您还可以从模型中删除非惟一索引、未使用的序列和导航属性、可选标量属性和约束名称。
您还可以在单独的文件中使用另一个分部类(partial class)添加额外的构造函数、方法、属性等。即使您打算再次对模型进行反向工程,这种方法也仍然有效。
8、更新模型
在对数据库进行更改之后,您可能需要更新EF核心模型来反映这些更改。如果数据库更改很简单,那么最简单的方法就是手动对EF核心模型进行更改。例如,重命名表或列、删除列或更新列的类型都是在代码中要做的微小更改。
然而,手动进行更重要的更改并不容易。一个常见的工作流是使用-Force (PMC)或——force (CLI)从数据库再次逆向工程模型,用更新的模型覆盖现有的模型。
如果您从数据库中再次对模型进行反向工程,则对这些文件所做的任何更改都将丢失。