实体框架7(EF7)预览版5已经发货,支持表-每-具体类型(TPC)的映射。这篇博文将重点介绍TPC。预览5中还包括其他一些增强功能,例如:
- 支持SQL Server中的AT时区
- 对命令和连接拦截的更新(#23087,#23085,#17261)
- 增加了删除行为属性
表-每具体类型(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 | 猫科动物 | 猫 | 不适用 | 猕猴桃 | BA | NULL |
| 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 | 价值 |
|---|---|
| 4 | 100.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列,而Cats和Dogs都有一个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查询。这些查询是:
- 一个返回层次结构中所有类型的实体的查询。
context.Animals.Where(a => a.Species.StartsWith("F")).ToList();
- 返回层次结构中某一类型子集的实体的查询。
context.Pets.Where(a => a.Species.StartsWith("F")).ToList();
- 只返回层次结构中单一叶子类型的实体的查询。
context.Cats.Where(a => a.Species.StartsWith("F")).ToList();
当使用TPH时,在所有情况下生成的SQL都是简单而有效的。为了帮助提高这些查询的性能,可以考虑定义一个索引,以便在判别器上进行快速过滤。在某些情况下,与表扫描相比,这可能是查询速度下降的来源。然而,请注意,如果索引的选择性不高,SQL Server会避免使用它。例如,对于5个超过100万行的判别器值,SQL服务器更倾向于使用表扫描。请注意,添加索引也会减慢更新速度,这一点可能重要,也可能不重要。
最后,如果你的数据库系统支持它,那么当大多数行对该列来说将是空的时候,可以考虑使用稀疏列。
- 所有类型。
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%'
- 类型的子集。
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%')
- 叶子类型。
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 JOIN 。UNION 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.SqlServer | Microsoft SQL Server和SQL Azure的数据库提供商。 |
| Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite | SQL Server对空间类型的支持 |
| Microsoft.EntityFrameworkCore.Sqlite | SQLite的数据库提供者,包括数据库引擎的本地二进制文件 |
| Microsoft.EntityFrameworkCore.Sqlite.Core | 用于SQLite的数据库提供者,没有打包的本地二进制文件 |
| Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite | SQLite对空间类型的支持 |
| 微软.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.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
在使用旧版本的EF Core运行时的项目中使用这个新版本的EF7 CLI是可能的。
日常构建
EF7预览版与.NET 7预览版是一致的。这些预览版往往滞后于EF7的最新工作。考虑使用日常构建来获得最新的EF7功能和错误修复。
与预览版一样,日常构建需要.NET 6。
.NET数据社区的准备工作
.NET数据团队现在每隔周三在太平洋时间上午10点,东部时间下午1点,或UTC时间17点进行直播。加入流媒体,就您选择的数据相关主题提出问题,包括最新的预览版:
- 观看我们在YouTube上的节目播放列表
- 访问.NET社区Standup页面,预览即将到来的节目
- 提交您对嘉宾、产品、演示或其他内容的想法。
文档和反馈
所有EF Core文档的起点是docs.microsoft.com/ef/。
请在dotnet/efcore GitHub repo上提交发现的问题和任何其他反馈。
有用的链接
以下链接是为方便参考和访问而提供的。