Entity Framework 7 Preview 5中TPC功能详细介绍

314 阅读18分钟

实体框架7(EF7)预览版5已经发货,支持表-每-具体类型(TPC)的映射。这篇博文将重点介绍TPC。预览5中还包括其他一些增强功能,例如:

阅读EF7预览5增强功能的完整列表

表-每具体类型(TPC)映射

默认情况下,EF Core将一个.NET类型的继承层次映射到一个单一的数据库表。这被称为表-每-层次(TPH)映射策略。EF Core 5.0引入了table-per-type(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; }
}

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

此外,根据每个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])
);

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

Id品种鉴别器价值名称教育水平最喜欢的玩具
1猫科动物爱丽丝硕士NULL
2猫科动物不适用猕猴桃BANULL
3熟悉的犬类犬类吐司松鼠先生
4羚羊农场动物100.00

请注意。

  • Discriminator 列中的值表示保存的C#对象的类型
  • 层次结构中的每个属性都有一列
  • 如果所保存的对象类型的属性不存在,那么数据库中该列的值就是空的。

TPH策略要求,对于任何未在层次结构的根类型中定义的属性,数据库列都是空的,即使该属性是必需的。我们可以为这些列创建一个数据库约束,以确保每当具有该属性的实例被保存时,其值是非空的,但这不是由EF Core自动完成的。更多信息请参见EF Core GitHub repo上的问题#20931

TPT策略

在TPT策略中,为层次结构中的每个类型创建不同的表--因此被称为 "table-per-type"。表本身是用来确定保存对象的类型,每个表只包含该类型的属性列。比如说:

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])
);

将相同的数据保存到这个数据库中,结果如下:

动物表

Id物种
1猫科动物
2猫科动物
3熟悉的犬类
4白羊座

农场动物表

ID价值
4100.00

宠物表

ID名称
1爱丽丝
2麦当劳
3吐司

猫表

Id教育水平
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])
);

请注意,。

  • 没有关于Animal 的表,因为它是对象模型中的一个抽象类型。请记住,C#不允许抽象类型的实例,因此不存在将其保存到数据库的情况。
  • 基准类型中的属性映射对于每个具体类型都是重复的--例如,每个表都有一个Species 列,而CatsDogs 都有一个Name 列。

将相同的数据保存到这个数据库中的结果如下:

FarmAnimals表

Id品种价值
4羚羊100.00

Pets table

身份证品种名称

猫咪表

ID品种名称教育水平
1猫科动物爱丽丝硕士
2猫科动物硕士

犬类表

身份证品种名称最喜欢的玩具
3熟悉的犬科动物吐司松鼠先生

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

在EF Core中配置继承映射

当映射一个继承层次结构时,层次结构中的所有类型必须明确地包含在模型中。这可以通过在你的DbContext ,为该类型创建一个DbSet 属性来实现。

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; }

或者通过使用OnModelCreating 中的Entity 方法:

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 。例如:要使用TPT,将其改为:

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

同样,UseTpcMappingStrategy ,用于配置TPC:

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

在每一种情况下,每个类型要使用的表名可以使用ToTable builder方法,或者[Table] 属性来配置。然而,这只对那些被映射到正在使用的策略的表的类型有效。例如,下面的代码指定了TPC映射的表名。

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

不能为Animal ,因为在使用TPC策略时,它没有被映射到自己的表中。相反,当使用TPH策略时,只有基本类型(Animal)可以被赋予一个表名。

如果一个层次结构中的多个类型被赋予不同的表名,但没有明确指定映射策略,那么就使用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要求层次结构中的所有实体必须有一个唯一的键值,即使这些实体有不同的类型。Id 因此,使用我们的例子模型,一个Dog 和一个Cat 不能有相同的键值。 其次,与TPT不同,没有一个公共的表可以作为键值存在和可以生成的唯一地方。这意味着不能使用一个简单的Identity 列。

对于支持序列的数据库,这个键值可以通过使用一个单一的序列并在每个表的默认约束中引用它来生成。这就是上面显示的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映射都能正常工作,这就是为什么它是默认的。人们常常担心表会变得非常宽,许多列只有稀疏的填充。虽然这可能是真的,但对于现代数据库系统来说,这很少是一个问题。使用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会避免使用它。例如,对于5个超过100万行的判别器值,SQL服务器更倾向于使用表扫描。请注意,添加索引也会减慢更新速度,这一点可能重要,也可能不重要。

最后,如果你的数据库系统支持它,那么当大多数行对该列来说将是空的时候,可以考虑使用稀疏列。

  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策略很少是一个好的选择。它主要用于认为数据以规范化的形式存储是很重要的时候,而这又往往是遗留的现有数据库或独立于应用开发团队管理的数据库的情况。

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列("子表")返回空值,而这不是正确的类型。因此,对于一只狗来说,[d].[Id] 将是非空的,而所有其他(具体)的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

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

再次使用相同的LINQ查询,在查询所有类型的实体时需要的SQL比TPT要好,因为它在查询中需要少一个表。这是因为没有抽象的基本类型的表。此外,UNION ALL ,而不是TPT所需要的LEFT JOINUNION ALL ,不需要执行任何行之间的匹配或行的去重,这使得它比TPT查询中使用的连接更有效率。

综上所述,与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框架上运行。

EF7是EF Core 6.0的后续版本,不能与EF6混淆。如果你正在考虑从EF6升级,请阅读我们的指南,从EF6移植到EF Core

如何获得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的数据库提供商。
Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuiteSQL Server对空间类型的支持
Microsoft.EntityFrameworkCore.SqliteSQLite的数据库提供者,包括数据库引擎的本地二进制文件
Microsoft.EntityFrameworkCore.Sqlite.Core用于SQLite的数据库提供者,没有打包的本地二进制文件
Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuiteSQLite对空间类型的支持
微软.EntityFrameworkCore.Cosmos用于Azure Cosmos DB的数据库提供商
Microsoft.EntityFrameworkCore.InMemory内存数据库提供者
微软.EntityFrameworkCore.Tools用于Visual Studio Package Manager Console的EF Core PowerShell命令;使用它可以将脚手架迁移等工具与Visual Studio集成起来。
Microsoft.EntityFrameworkCore.Design用于EF Core工具的共享设计时组件
Microsoft.EntityFrameworkCore.Proxies懒惰的加载和变化跟踪的代理
Microsoft.EntityFrameworkCore.Abstractions解耦的EF Core抽象;使用它来实现EF Core定义的扩展数据注释等功能。
Microsoft.EntityFrameworkCore.Relational用于关系型数据库提供者的共享的EF Core组件
Microsoft.EntityFrameworkCore.Analyzers用于EF Core的C#分析器

我们还发布了用于ADO.NETMicrosoft.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 

在使用旧版本的EF Core运行时的项目中使用这个新版本的EF7 CLI是可能的。

日常构建

EF7预览版与.NET 7预览版是一致的。这些预览版往往滞后于EF7的最新工作。考虑使用日常构建来获得最新的EF7功能和错误修复。

与预览版一样,日常构建需要.NET 6。

.NET数据社区的准备工作

.NET数据团队现在每隔周三在太平洋时间上午10点,东部时间下午1点,或UTC时间17点进行直播。加入流媒体,就您选择的数据相关主题提出问题,包括最新的预览版:

文档和反馈

所有EF Core文档的起点是docs.microsoft.com/ef/

请在dotnet/efcore GitHub repo上提交发现的问题和任何其他反馈。

有用的链接

以下链接是为方便参考和访问而提供的。