宣布发布Entity Framework 7 预览版 5

Entity Framework 7 (EF7) 预览版 5 附带了对每个具体表类型(TPC) 映射的支持。这篇博客文章将重点介绍 TPC。预览版 5 中还包括其他几项增强功能,例如:

  • 支持 SQL Server 中的 AT 时区 https://github.com/dotnet/efcore/issues/26199
  • 命令和连接拦截更新 (https://github.com/dotnet/efcore/issues/23087, https://github.com/dotnet/efcore/issues/23085, https://github.com/dotnet/efcore/issues/17261)
  • 添加删除行为属性 https://github.com/dotnet/efcore/issues/9621

阅读 EF7 预览版 5 增强功能的完整列表 https://github.com/dotnet/efcore/issues?q=is%3Aissue+milestone%3A7.0.0-preview5+is%3Aclosed+label%3Atype-enhancement

每混凝土表类型 (TPC) 映射

默认情况下,EF Core 将 .NET 类型的继承层次结构映射到单个数据库表。这称为每层次结构表 (TPH) 映射策略。EF Core 5.0 引入了按类型表 (TPT) 策略,该策略支持将每个 .NET 类型映射到不同的数据库表。在 EF Core 7.0 预览版 5.0 中,我们很高兴地引入每具体类型表 (TPC) 策略。TPC 还将 .NET 类型映射到不同的表,但其方式解决了 TPT 策略的一些常见性能问题。

在这篇文章中,我们将首先描述 TPH、TPT 和 TPC 映射的结构,然后了解如何在 EF Core 中配置这些策略,最后讨论每种方法的优缺点。

映射继承层次结构

请考虑以下面向对象的域模型:

public abstract class Animal
{
    public int Id { get; set; }
    public string Species { get; set; }
}

public class FarmAnimal : Animal
{
    public decimal Value { get; set; }
}

public class Pet : Animal
{
    public string Name { get; set; }
}

public class Cat : Pet
{
    public string EducationLevel { get; set; }
}

public class Dog : Pet
{
    public string FavoriteToy { get; set; }
}

如果我们要从数据库中检索一些对象,那么我们必须知道它是哪种类型的动物。我们不想救一只猫,然后把它当作狗来读回来,反之亦然。(我可以从经验中告诉你,狗通常不喜欢被当作猫对待,猫当然不喜欢被当作狗来对待!因此,这意味着必须以某种形式将动物类型(即在 C# 中创建动物时使用的实际类)保存到数据库中。Animal

此外,根据其类型,与每个对象相关联不同的信息。例如,在我们的模型中,农场动物具有一定的货币价值但没有名称,而宠物是无价的和命名的。Animal

继承映射策略(TPH、TPT 或 TPC)定义了如何将这种面向对象的类型信息和特定于类型的信息保存到关系数据库中,其中继承不是一个自然的概念。

TPH战略

使用 TPH 策略,将为层次结构中的所有类型创建单个表,因此称为“每个层次结构的表”。此表包含一个包含“鉴别器值”的特殊列,该值指示每行中保存的对象的类型。此外,还会为层次结构中每种类型的每个属性创建一列。例如:

CREATE TABLE [Animals] (
    [Id] int NOT NULL IDENTITY,
    [Species] nvarchar(max) NOT NULL,
    [Discriminator] nvarchar(max) NOT NULL,
    [Value] decimal(18,2) NULL,
    [Name] nvarchar(max) NULL,
    [EducationLevel] nvarchar(max) NULL,
    [FavoriteToy] nvarchar(max) NULL,
    CONSTRAINT [PK_Animals] PRIMARY KEY ([Id])
);

将两只猫、一只狗和一只羊保存到此表中,结果如下:

编号物种鉴别器价值名字教育级别收藏玩具
1费利斯·卡图斯爱丽丝工商管理硕士
2费利斯·卡图斯苹果电脑
3熟悉的犬科动物吐司松鼠先生
4绵羊农场动物100.00

请注意:

  • 列中的值指示保存的 C# 对象的类型Discriminator
  • 层次结构中的每个属性都有一列
  • 如果所保存对象的类型不存在该属性,则该列的数据库中值为 null

TPH 策略要求对于层次结构的根类型中未定义的任何属性,数据库列可以为 null,即使该属性是必需的。可以为这些列创建数据库约束,以确保每当保存具有该属性的实例时,该值都为非 null,但 EF Core 不会自动执行此操作。

TPT战略

使用 TPT 策略,将为层次结构中的每个类型创建不同的表,因此称为“按类型表”。表本身用于确定所保存对象的类型,每个表仅包含该类型属性的列。例如:

CREATE TABLE [Animals] (
    [Id] int NOT NULL IDENTITY,
    [Species] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Animals] PRIMARY KEY ([Id])
);

CREATE TABLE [FarmAnimals] (
    [Id] int NOT NULL,
    [Value] decimal(18,2) NOT NULL,
    CONSTRAINT [PK_FarmAnimals] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_FarmAnimals_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([Id])
);

CREATE TABLE [Pets] (
    [Id] int NOT NULL,
    [Name] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Pets] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Pets_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([Id])
);

CREATE TABLE [Cats] (
    [Id] int NOT NULL,
    [EducationLevel] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Cats] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Cats_Pets_Id] FOREIGN KEY ([Id]) REFERENCES [Pets] ([Id])
);

CREATE TABLE [Dogs] (
    [Id] int NOT NULL,
    [FavoriteToy] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Dogs] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Dogs_Pets_Id] FOREIGN KEY ([Id]) REFERENCES [Pets] ([Id])
);

将相同的数据保存到此数据库中会产生以下结果:

动物表:

编号物种
1费利斯·卡图斯
2费利斯·卡图斯
3熟悉的犬科动物
4绵羊

农场动物表:

编号价值
4100.00

宠物表:

编号名字
1爱丽丝
2苹果电脑
3吐司

猫表:

编号教育级别
1工商管理硕士
2

狗表:

编号收藏玩具
3松鼠先生

请注意,数据以规范化形式保存,但这意味着单个对象的信息分布在多个表中。

TPC 映射

TPC 策略与 TPT 策略类似,不同之处在于为层次结构中的每个具体类型创建了不同的表,但不是为抽象类型创建表,因此称为“每个具体类型的表”。与 TPT 一样,表本身指示所保存对象的类型。但是,与 TPT 映射不同,每个表都包含具体类型及其基类型中每个属性的列。例如:

CREATE TABLE [FarmAnimals] (
    [Id] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalIds]),
    [Species] nvarchar(max) NOT NULL,
    [Value] decimal(18,2) NOT NULL,
    CONSTRAINT [PK_FarmAnimals] PRIMARY KEY ([Id])
);

CREATE TABLE [Pets] (
    [Id] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalIds]),
    [Species] nvarchar(max) NOT NULL,
    [Name] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Pets] PRIMARY KEY ([Id])
);

CREATE TABLE [Cats] (
    [Id] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalIds]),
    [Species] nvarchar(max) NOT NULL,
    [Name] nvarchar(max) NOT NULL,
    [EducationLevel] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Cats] PRIMARY KEY ([Id])
);

CREATE TABLE [Dogs] (
    [Id] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalIds]),
    [Species] nvarchar(max) NOT NULL,
    [Name] nvarchar(max) NOT NULL,
    [FavoriteToy] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Dogs] PRIMARY KEY ([Id])
);

请注意:

  • 没有 的表,因为它是对象模型中的抽象类型。请记住,C# 不允许使用抽象类型的实例,因此不存在将抽象类型实例保存到数据库的情况。Animal
  • 对每个具体类型重复基类型中的属性映射,例如,每个表都有一列,并且两者都有一列。SpeciesCatsDogsName

将相同的数据保存到此数据库中会产生以下结果:

农场动物表:

编号物种价值
4绵羊100.00

宠物表:

编号物种名字

猫表:

编号物种名字教育级别
1费利斯·卡图斯爱丽丝工商管理硕士
2费利斯·卡图斯苹果电脑

狗表:

编号物种名字收藏玩具
3熟悉的犬科动物吐司松鼠先生

请注意,与 TPT 映射不同,单个对象的所有信息都包含在单个表中。

在 EF Core 中配置继承映射

映射继承层次结构时,层次结构中的所有类型都必须显式包含在模型中。这可以通过为以下类型创建属性来完成:DbSetDbContext

public DbSet<Animal> Animals { get; set; }
public DbSet<Pet> Pets { get; set; }
public DbSet<Cat> Cats { get; set; }
public DbSet<Dog> Dogs { get; set; }
public DbSet<FarmAnimal> FarmAnimals { get; set; }

或者使用以下方法:EntityOnModelCreating

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Animal>();
    modelBuilder.Entity<Pet>();
    modelBuilder.Entity<Cat>();
    modelBuilder.Entity<Dog>();
    modelBuilder.Entity<FarmAnimal>();
}

这与旧版 EF6 行为不同,在旧版 EF6 行为中,有时会自动发现映射基类型的派生类型。

无需执行任何其他操作即可将层次结构映射为 TPH,因为它是默认策略。但是,可以通过调用层次结构的基类型来显式执行此操作。例如:UseTphMappingStrategy

modelBuilder.Entity<Animal>().UseTphMappingStrategy();

要改用 TPT,请将此值更改为 。例如:UseTptMappingStrategy

modelBuilder.Entity<Animal>().UseTptMappingStrategy();

同样,用于配置 TPC:UseTpcMappingStrategy

modelBuilder.Entity<Animal>().UseTpcMappingStrategy();

在每种情况下,都可以使用生成器方法或属性来配置要用于每种类型的表名。但是,这仅对映射到所用策略的表的类型有效。例如,下面的代码指定 TPC 映射的表名:ToTable[Table]

modelBuilder.Entity<Pet>().ToTable("Pets");
modelBuilder.Entity<Cat>().ToTable("Cats");
modelBuilder.Entity<Dog>().ToTable("Dogs");
modelBuilder.Entity<FarmAnimal>().ToTable("FarmAnimals");

不能为其指定表名,因为在使用 TPC 策略时,它不会映射到自己的表。相反,使用 TPH 策略时,只能为基类型 () 指定表名。AnimalAnimal

如果层次结构中的多个类型被赋予不同的表名,但没有显式指定映射策略,则使用 TPT 策略。这是在 EF7 之前配置 TPT 的正常方法。

主键

所选的继承映射策略对主键值的生成和管理方式有影响。TPH 中的键很容易,因为每个实体实例都由单个表中的单个行表示。可以使用任何类型的键值生成,并且不需要其他约束。

对于 TPT 策略,表中始终有一行映射到层次结构的基本类型。任何类型的密钥生成都可以在此行上使用。其他表的键使用外键约束链接到此表。例如:

CREATE TABLE [FarmAnimals] (
    [Id] int NOT NULL,
    [Value] decimal(18,2) NOT NULL,
    CONSTRAINT [PK_FarmAnimals] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_FarmAnimals_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([Id])
);

这可确保对层次结构的每个表中的给定实体使用相同的主键值。

当使用TPC策略时,这会变得更加复杂。首先,请务必了解 EF Core 要求层次结构中的所有实体必须具有唯一的键值,即使实体具有不同的类型也是如此。因此,使用我们的示例模型,a 不能具有与 相同的键值。其次,与 TPT 不同,没有一个公共表可以充当键值所在的单个位置并可以生成。这意味着不能使用简单列。DogIdCatIdentity

对于支持序列的数据库,可以通过使用单个序列并在每个表的默认约束中引用它来生成此键值。这是上面显示的 TPC 表中使用的策略,其中每个表具有以下各项:

[Id] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalIds])

AnimalIds是由 EF Core 迁移创建的序列。以下模型构建代码为 SQL Server 设置了此设置:

modelBuilder.HasSequence<int>("AnimalIds");

modelBuilder.Entity<Animal>()
    .UseTpcMappingStrategy()
    .Property(e => e.Id).HasDefaultValueSql("NEXT VALUE FOR [AnimalIds]");

对于数据库系统,默认约束的语法可能不同。

映射策略的优缺点

以上所有内容可能都很有趣,但是您如何决定使用哪种策略?

断续器

在几乎所有情况下,TPH映射都可以正常工作,这就是为什么它是默认值的原因。人们经常担心表可能会变得非常宽,许多列只是稀疏地填充。虽然这可能是真的,但对于现代数据库系统来说,这很少是问题。TPH的查询性能总是非常好,主要是因为无论您编写什么查询,都只需要一个表即可返回结果。

不同策略最重要的性能差异源于不同类型的常见查询所需的 SQL。为了说明这一点,我们将对每个策略运行相同的三个 LINQ 查询。这些查询是:

  1. 返回层次结构中所有类型的实体的查询:
context.Animals.Where(a => a.Species.StartsWith("F")).ToList();
  1. 从层次结构中的类型子集返回实体的查询:
context.Pets.Where(a => a.Species.StartsWith("F")).ToList();
  1. 仅从层次结构中的单个叶类型返回实体的查询:
context.Cats.Where(a => a.Species.StartsWith("F")).ToList();

使用TPH时,生成的SQL在所有情况下都是简单而有效的。为了帮助提高这些查询的性能,请考虑定义一个索引,以便更快地筛选鉴别器。在某些情况下,与表扫描相比,这可能是查询速度变慢的根源。但是,请注意,如果索引不是高度选择性的,则 SQL Server 会避免使用索引。例如,对于超过 100 万行的 5 个鉴别器值,SQL Server 首选表扫描。请注意,添加索引也会减慢更新速度,这可能很重要,也可能不重要。

最后,如果数据库系统支持它,则考虑使用稀疏列,因为该列的大多数行将为 null。

  1. 所有类型:
SELECT [a].[Id], [a].[Discriminator], [a].[Species], [a].[Value], [a].[Name], [a].[EducationLevel], [a].[FavoriteToy]
FROM [Animals] AS [a]
WHERE [a].[Species] LIKE N'F%'
  1. 类型子集:
SELECT [a].[Id], [a].[Discriminator], [a].[Species], [a].[Name], [a].[EducationLevel], [a].[FavoriteToy]
FROM [Animals] AS [a]
WHERE [a].[Discriminator] IN (N'Pet', N'Cat', N'Dog') AND ([a].[Species] LIKE N'F%')
  1. 叶型:
SELECT [a].[Id], [a].[Discriminator], [a].[Species], [a].[Name], [a].[EducationLevel]
FROM [Animals] AS [a]
WHERE [a].[Discriminator] = N'Cat' AND ([a].[Species] LIKE N'F%')

断续器

TPT策略很少是一个好的选择。它主要用于认为数据以规范化形式存储的重要性,而对于传统的现有数据库或独立于应用程序开发团队管理的数据库,情况通常是这种情况。

TPT 策略的主要问题是,几乎所有查询都涉及联接多个表,因为任何给定实体实例的数据都拆分到多个表中。

再次使用相同的查询,我们可以看到查询所有类型的实体需要联接所有五个表:

SELECT [a].[Id], [a].[Species], [f].[Value], [p].[Name], [c].[EducationLevel], [d].[FavoriteToy], CASE
    WHEN [d].[Id] IS NOT NULL THEN N'Dog'
    WHEN [c].[Id] IS NOT NULL THEN N'Cat'
    WHEN [p].[Id] IS NOT NULL THEN N'Pet'
    WHEN [f].[Id] IS NOT NULL THEN N'FarmAnimal'
END AS [Discriminator]
FROM [Animals] AS [a]
    LEFT JOIN [FarmAnimals] AS [f] ON [a].[Id] = [f].[Id]
    LEFT JOIN [Pets] AS [p] ON [a].[Id] = [p].[Id]
    LEFT JOIN [Cats] AS [c] ON [a].[Id] = [c].[Id]
    LEFT JOIN [Dogs] AS [d] ON [a].[Id] = [d].[Id]
WHERE [a].[Species] LIKE N'F%'

EF Core 使用“鉴别器综合”来确定数据来自哪个表,从而确定要使用的正确类型。这之所以有效,是因为 LEFT JOIN 返回依赖 ID 列(“子表”)的 null,这些列不是正确的类型。因此,对于狗,将是非空的,所有其他(具体)ID将是空的。[d].[Id]

查询类型子集的实体仍需要联接基表,从而导致使用四个表:

SELECT [a].[Id], [a].[Species], [p].[Name], [c].[EducationLevel], [d].[FavoriteToy], CASE
    WHEN [d].[Id] IS NOT NULL THEN N'Dog'
    WHEN [c].[Id] IS NOT NULL THEN N'Cat'
    WHEN [p].[Id] IS NOT NULL THEN N'Pet'
END AS [Discriminator]
FROM [Animals] AS [a]
    LEFT JOIN [Pets] AS [p] ON [a].[Id] = [p].[Id]
    LEFT JOIN [Cats] AS [c] ON [a].[Id] = [c].[Id]
    LEFT JOIN [Dogs] AS [d] ON [a].[Id] = [d].[Id]
WHERE ([d].[Id] IS NOT NULL OR [c].[Id] IS NOT NULL OR [p].[Id] IS NOT NULL) AND ([a].[Species] LIKE N'F%')

即使只查询单个叶类型的实体,也需要叶类型派生自的所有类型的表:

SELECT [a].[Id], [a].[Species], [p].[Name], [c].[EducationLevel], CASE
    WHEN [c].[Id] IS NOT NULL THEN N'Cat'
END AS [Discriminator]
FROM [Animals] AS [a]
    LEFT JOIN [Pets] AS [p] ON [a].[Id] = [p].[Id]
    LEFT JOIN [Cats] AS [c] ON [a].[Id] = [c].[Id]
WHERE [c].[Id] IS NOT NULL AND ([a].[Species] LIKE N'F%')

断续器

TPC 策略是对 TPT 的改进,因为它确保给定实体实例的信息始终存储在单个表中。这意味着,当映射的层次结构很大并且具有许多具体(通常是叶)类型(每个类型都具有大量属性)并且在大多数查询中仅使用一小部分类型时,TPC 策略可能很有用。

再次使用相同的 LINQ 查询,查询所有类型的实体时所需的 SQL 比 TPT 更好,因为它在查询中需要的表更少。这是因为抽象基类型没有表。此外,用于代替 TPT 所需的。 不需要在行之间执行任何匹配或对行执行重复数据消除,这使得它比 TPT 查询中使用的联接更有效。UNION ALLLEFT JOINUNION ALL

话虽如此,与TPH的SQL相比,在这种情况下,TPC的SQL仍然不是很好:

SELECT [t].[Id], [t].[Species], [t].[Value], [t].[Name], [t].[EducationLevel], [t].[FavoriteToy], [t].[Discriminator]
FROM (
    SELECT [f].[Id], [f].[Species], [f].[Value], NULL AS [Name], NULL AS [EducationLevel], NULL AS [FavoriteToy], N'FarmAnimal' AS [Discriminator]
    FROM [FarmAnimals] AS [f]
    UNION ALL
    SELECT [p].[Id], [p].[Species], NULL AS [Value], [p].[Name], NULL AS [EducationLevel], NULL AS [FavoriteToy], N'Pet' AS [Discriminator]
    FROM [Pets] AS [p]
    UNION ALL
    SELECT [c].[Id], [c].[Species], NULL AS [Value], [c].[Name], [c].[EducationLevel], NULL AS [FavoriteToy], N'Cat' AS [Discriminator]
    FROM [Cats] AS [c]
    UNION ALL
    SELECT [d].[Id], [d].[Species], NULL AS [Value], [d].[Name], NULL AS [EducationLevel], [d].[FavoriteToy], N'Dog' AS [Discriminator]
    FROM [Dogs] AS [d]
) AS [t]
WHERE [t].[Species] LIKE N'F%'

在查询类型子集的实体时,情况也是如此:

SELECT [t].[Id], [t].[Species], [t].[Name], [t].[EducationLevel], [t].[FavoriteToy], [t].[Discriminator]
FROM (
    SELECT [p].[Id], [p].[Species], [p].[Name], NULL AS [EducationLevel], NULL AS [FavoriteToy], N'Pet' AS [Discriminator]
    FROM [Pets] AS [p]
    UNION ALL
    SELECT [c].[Id], [c].[Species], [c].[Name], [c].[EducationLevel], NULL AS [FavoriteToy], N'Cat' AS [Discriminator]
    FROM [Cats] AS [c]
    UNION ALL
    SELECT [d].[Id], [d].[Species], [d].[Name], NULL AS [EducationLevel], [d].[FavoriteToy], N'Dog' AS [Discriminator]
    FROM [Dogs] AS [d]
) AS [t]
WHERE [t].[Species] IS NOT NULL AND ([t].[Species] LIKE N'F%')

但是,在查询单个叶类型的实体时,TPC 比 TPT 好得多,因为这些实体的所有信息都来自单个表:

SELECT [c].[Id], [c].[Species], [c].[Name], [c].[EducationLevel]
FROM [Cats] AS [c]
WHERE [c].[Species] LIKE N'F%'

这些针对单叶类型的查询是 TPC 真正擅长的地方。

指导

总之,使用哪种映射策略的指南非常简单:

  • 如果您的代码主要查询单个叶类型的实体,请使用 TPC。这是因为:
    • 存储要求较小,因为没有空列,也没有鉴别器。
    • 鉴别器列上不需要索引,这会减慢更新速度,并可能降低查询速度。使用TPH时可能也不需要索引,但这取决于各种因素。
  • 如果您的代码主要查询许多类型的实体,例如针对基类型编写查询,则倾向于 TPH。
    • 如果您的数据库系统支持它(例如.SQL Server),那么请考虑对很少填充的列使用稀疏列。
  • 仅当受到外部因素的限制时才使用 TPT。

先决条件

  • EF7 当前面向 .NET 6。
  • EF7 不会在 .NET Framework 上运行。

EF7 是 EF Core 6.0 的后继产品,不要与 EF6 混淆。

如何获取 EF7 预览

EF7 作为一组 NuGet 包以独占方式分发。例如,若要将 SQL Server 提供程序添加到项目中,可以通过 dotnet 工具使用以下命令:

dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 7.0.0-preview.5.22302.2

下表链接到 EF Core 包的预览版 5 版本,并介绍了它们的用途。

目的
Microsoft.EntityFrameworkCore独立于特定数据库提供程序的主 EF Core 包
Microsoft.EntityFrameworkCore.SqlServerMicrosoft SQL Server 和 SQL Azure 的 Database provider
Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite对空间类型的 SQL Server 支持
Microsoft.EntityFrameworkCore.SqliteSQLite 的数据库提供程序,包括数据库引擎的本机二进制文件
Microsoft.EntityFrameworkCore.Sqlite.Core带打包本机二进制文件的 SQLite 数据库提供程序
Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuiteSQLite 对空间类型的支持
Microsoft.EntityFrameworkCore.CosmosAzure Cosmos DB 的 Database provider
Microsoft.EntityFrameworkCore.InMemory内存中数据库提供程序
Microsoft.EntityFrameworkCore.ToolsVisual Studio Package Manager Console 的 EF Core PowerShell 命令;使用它来将脚手架和迁移等工具与Visual Studio集成
Microsoft.EntityFrameworkCore.Design适用于 EF Core 工具的共享设计时组件
Microsoft.EntityFrameworkCore.Proxyies延迟加载和更改跟踪代理
Microsoft.EntityFrameworkCore.Abstractions解耦的 EF 核心抽象;将其用于 EF Core 定义的扩展数据批注等功能
Microsoft.EntityFrameworkCore.Relational关系数据库提供程序的共享 EF 核心组件
Microsoft.EntityFrameworkCore.Analyzers适用于 EF 核心的 C# 分析器

我们还发布了适用于 ADO.NET Microsoft.Data.Sqlite.Core 提供程序的 7.0 预览版 5 版本。

安装 EF7 命令行界面 (CLI)

在执行 EF7 Core 迁移或基架命令之前,必须将 CLI 包作为全局或本地工具安装。

要全局安装预览工具,请使用以下命令进行安装:

dotnet tool install --global dotnet-ef --version 7.0.0-preview.5.22302.2 

如果已安装该工具,则可以使用以下命令对其进行升级:

dotnet tool update --global dotnet-ef --version 7.0.0-preview.5.22302.2 

可以将此新版本的 EF7 CLI 用于使用较旧版本的 EF Core 运行时的项目。

给TA打赏
共{{data.count}}人
人已打赏
.NET

宣布推出 .NET 7 预览版 5

2022-6-15 17:24:13

.NET

可观察性与监控:有什么区别

2022-6-16 17:02:09

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新私信 私信列表
搜索