介绍
在本文中,我们将讨论Entity Framework Core 5.0。Entity Framework并不是什么新东西,但是,在.NET Core 5.0中,微软在实体框架中引入了许多新的特性或功能,这些特性或功能将在开发过程中帮助开发人员。Entity Framework Core是一个现代的基于对象的数据库映射 .NET Core。它支持很多东西,比如LINQ查询、变更跟踪、数据模型更新和基于模式的迁移。Entity Framework Core可以与许多数据库一起工作,如SQL数据库(本地和Azure版本),SQLite MySQL, PostgreSQL,和Azure Cosmos DB。Entity Framework (EF) Core是现有实体框架数据访问技术的轻量级、可扩展、开源和跨平台版本。在本文中,我们将讨论有关.NET Core 5.0中引入的新特性。
Entity Framework概述
在.NET 3.5版本中,开发人员主要使用或记录ADO.NET与网络相关的代码,用于从数据库中保存或检索应用程序数据。为此,我们需要打开到数据库的连接,创建一个数据集来获取或发送数据到数据库,然后将数据集转换为.NET Objects,反之亦然。整个过程非常漫长且容易出错。作为这种方法的替代方案,Microsoft提供了一个名为Entity Framework的框架,它可以为我们的应用程序自动化所有这些与数据库相关的活动。
Entity Framework是一个基于ORM的开源框架,基于网络的应用程序。在这个框架的帮助下,开发人员可以使用域特定类的对象处理数据,而不必关注相关的数据库表和列。在实体框架的帮助下,开发人员在处理数据时可以在更高的抽象级别上工作,并且可以通过编写比传统应用程序更少的代码来创建和维护面向数据的应用程序。
如上图所示,实体框架适合于业务实体(域类)和数据库。它保存存储在业务实体属性中的数据,并从数据库中检索数据,并自动将其转换为业务实体对象。
Entity Framework的特点
实体框架有几个特性可以帮助开发人员通过编写更少的代码来开发任何应用程序。一些最重要的特征如下:
- Entity Framework是一个跨平台的框架,可以在Windows、Linux和Mac上同时运行。
- 实体框架总是创建基于EDM(Entity Data Mode)的实体,获取/设置不同数据类型的属性。该实体由框架在查询或将数据保存到数据库时使用。
- 实体框架支持LINQ查询从数据库检索数据。这个框架还允许我们直接对数据库执行原始SQL查询,以执行查询操作。
- 当我们调用SaveChanges()方法时,实体框架总是根据实体发生的更改对数据库执行插入、更新和删除命令。实体框架还提供了异步SaveChangesAsync()方法。
- 在查询或保存数据时,实体框架总是自动执行事务管理。它还提供了定制事务管理的选项。
- 实体框架提供了开箱即用的第一级缓存。因此,重复查询将从缓存返回,而不是数据库。
- 实体框架通过使用数据注释属性或Fluent API重写默认约定来配置实体框架模型。
Entity Framework Core 5.0概述
Entity Framework Core是EF 6.0之后的新版本。EF Core是开源的、轻量级的、可扩展的、基于跨平台的实体框架数据访问技术。实体框架是基于对象/关系映射(ORM)的框架。它是ADO的一个增强。NET为开发人员提供了一种自动访问数据并将数据存储到数据库中的机制。Entity Framework core主要用于开发基于 .NET Core的应用程序。尽管如此,我们仍然可以使用实体框架核心来开发标准的.NET 4.5或以上的应用程序。下图展示了受支持的应用程序类型、.Net Framework和操作系统。
Entity Framework Core 5.0是针对.NET Core应用程序的实体框架的新版本和改进版本。由于是新版,它还没有像EF 6.0那样成熟。与EF 6.0相比,EF Core继续支持以下特性和概念:
- DBSet & DBContext
- Data Model(数据模型)
- 查询使用Linq-To-Entities
- 更改跟踪(Change Tracking)
- SaveChanges
- 迁移(Migrations)
但是,有一些特性或功能在实体框架核心中是不支持的,如下所示:
- 模型的图形可视化
- 用于DB-First方法的实体数据模型向导
- ObjectContext API或使用实体SQL查询
- 自动迁移
- 继承:TPT(每个类型的表) & TPC(每个具体类的表)
- 实体分割
- 空间数据
- 延迟加载相关数据
- 用于CRUD操作的存储过程与DBContext的映射
- 种子数据
此外,Entity Framework Core提供了以下EF 6.0不支持的新特性或功能:
- 批量插入、更新和删除操作
- 用于测试的内存提供程序
- 支持IoC(控制反转)
- 唯一约束的实现
- 阴影属性
- 替代键
- 全局查询过滤器
- 字段映射
- DBContext池
- 更好地去处理断开连接的实体图
EF核心数据库供应商
Entity Framework Core使用提供者模型来访问许多不同的数据库。EF Core包含了你需要安装的NuGet包。下表列出了EF Core的数据库提供程序和NuGet包。
EF Core 5.0有什么新内容?
由于Entity Framework Core 5.0是主要发行版,它包含了许多新特性。EF Core 5.0版本包含了许多破坏性的更改,这些更改主要是针对API的改进或与现有应用程序相关的行为更改。
Many – to – Many
在Entity Framework 5.0中,一个主要特性是多对多关系,而不需要显式地映射连接表。在EF Core的前一个版本中,我们需要定义第三个实体来建立多对多关系。根据示例,如果我们想在Author实体和Blogs实体之间建立多对多关系,那么我们需要定义三个实体——Author、Blog和AuthorBlog。但是在EF Core 5.0中,没有必要定义第三个实体,即AuthorBlog。现在,我们可以定义作者和Blogs实体之间的多对多关系。为了定义这两个实体之间的多对多关系,我们需要定义提及实体类,如下所示:
public class Author
{
public int Id { get; set; }
public string Name { get; set; }
/* EF Relations */
public ICollection<Blog> Blogs { get; set; }
}
public class Blog
{
public int Id { get; set; }
public string Text { get; set; }
/* EF Relations */
public ICollection<Author> Authors { get; set; }
}
在上面的示例中,作者包含一组博客,而博客包含一组作者。在Entity Framework 5.0中,这被认为是一种多对多关系。因此,DBContext类按照上面的实体将看起来像这样:
public class EFCore5RelationshipsExamplesDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{ optionsBuilder.UseSqlServer(@"Server=(localdb)\MSSQLLocalDB;Database=TestDB;Trusted_Connection=True;MultipleActiveResultSets=true");
}
public DbSet<Author> Authors { get; set; }
public DbSet<Blog> Blogs { get; set; }
}
Split Queries
在Entity Framework Core 3.0中,EF Core总是为每个LINQ查询填充一个SQL查询。这通常确保事务模式约束内返回的数据的一致性。但是,当查询使用Include或投影返回多个相关记录或集合时,这可能会变得非常缓慢。但是在EF Core 5.0中,现在允许将包含相关集合的单个LINQ查询拆分为多个SQL查询。这可以显著提高应用程序的性能。实体框架现在允许我们在LINQ查询中指定是否要将其拆分为多个SQL查询。如果提到了split,那么split查询将为每个包含的集合生成一个额外的SQL查询,而不是JOINs.
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.AsSplitQuery()
.ToList();
}
它将产生以下SQL查询:
SELECT [b].[Id] as BlogId, [b].[Name]
FROM [Blogs] AS [b]
ORDER BY [b].[BlogId]
SELECT [p].[Id] as AuthorId, [p].[BlogId], [p].[Title], [b].[BlogId]
FROM [Blogs] AS [b]
INNER JOIN [Author] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId]
我们也可以在我们的应用程序上下文中将拆分查询配置为默认值:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer( @"Server=(localdb)\MSSQLLocalDB;Database=TestDB;Trusted_Connection=True;ConnectRetryCount=0",
o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));
}
Logging & Diagnostics
Entity Framework Core 5.0还引入了一种简单易行的方法,通过新的LogTo方法设置日志记录。通过这个方法,我们可以将消息记录到控制台,其中包括EF Core生成的所有SQL。在这种方法的帮助下,我们可以从EF核心应用程序生成日志,而不需要任何与外部日志框架相关的特殊配置。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.LogTo(Console.WriteLine);
有多种重载可以在不同的用例中使用:
- 设置最小日志级别
例如:.LogTo(Console.WriteLine, LogLevel.Information)
- 仅过滤特定事件
例如:LogTo(Console.WriteLine, new[] {CoreEventId.ContextInitialized, RelationalEventId.CommandExecuted})
- 过滤特定类别中的所有事件
例如:.LogTo(Console.WriteLine, new[] {DbLoggerCategory.Database.Name}, LogLevel.Information)
- 在事件和级别上使用自定义过滤器
例如:.LogTo(Console.WriteLine, (id, level) => id == RelationalEventId.CommandExecuting)
Table – per – type (TPT) mapping
一般来说,实体框架作为.NET类型的继承层次映射到单个数据库类型。它通常被称为逐层表(table-per-hierarchy, TPH)映射。但是在EF Core 5.0中,允许我们将继承层次结构中的每个.NET类类型映射到不同的数据库表中,这被称为每类型表(table-per-type, TPT)映射。每类型表继承通常在数据库中使用一个单独的表来维护继承层次结构中每个类型的非继承属性和键属性的数据。
- 按类型表(TPT)通常将继承关系表示为表中的关系外键
- 每个类和子类包括抽象类都有它的表
- 子类的表只包含每个非继承属性的列,以及一个主键,该主键也是基类表的外键
让我们考虑以下带有映射层次结构的简单类模型:
public class Person
{
public int Id { get; set; }
public string FullName { get; set; }
}
public class Student: Person
{
public DateTime EnrollmentDate { get; set; }
}
public class Teacher: Person
{
public DateTime JoiningDate { get; set; }
}
默认情况下,实体框架会将其映射为一个单独的表:
CREATE TABLE [dbo].[People] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[FullName] NVARCHAR (MAX) NULL,
[Discriminator] NVARCHAR (MAX) NOT NULL,
[EnrollmentDate] DATETIME2 (7) NULL,
[JoiningDate] DATETIME2 (7) NULL,
CONSTRAINT [PK_People] PRIMARY KEY CLUSTERED ([Id] ASC)
);
在每类型表映射模式中,所有实体类型都与单独的表进行映射。仅属于基类型或派生类型的属性存储在映射到该类型的表中。实体类型可以使用映射属性映射到不同的表。
[Table("People")]
public class Person
{
public int Id { get; set; }
public string FullName { get; set; }
}
[Table("Students")]
public class Student : Person
{
public DateTime EnrollmentDate { get; set; }
}
[Table("Teachers")]
public class Teacher : Person
{
public DateTime JoiningDate { get; set; }
}
现在,将每个实体类型映射到不同的表将导致每个类型对应一个表。
CREATE TABLE [dbo].[People] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[FullName] NVARCHAR (MAX) NULL,
CONSTRAINT [PK_People] PRIMARY KEY CLUSTERED ([Id] ASC)
);
CREATE TABLE [dbo].[Students] (
[Id] INT NOT NULL,
[EnrollmentDate] DATETIME2 (7) NOT NULL,
CONSTRAINT [PK_Students] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_Students_People_Id] FOREIGN KEY ([Id]) REFERENCES [dbo].[People] ([Id])
);
CREATE TABLE [dbo].[Teachers] (
[Id] INT NOT NULL,
[JoiningDate] DATETIME2 (7) NOT NULL,
CONSTRAINT [PK_Teachers] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_Teachers_People_Id] FOREIGN KEY ([Id]) REFERENCES [dbo].[People] ([Id])
);
灵活的实体映射
在实体框架中,实体类型主要映射到表或视图,以便EF Core在查询该类型时将拉出表或视图的记录。在EF Core 5.0中,现在我们有了额外的映射选项,可以用SQL查询(称为定义查询)或表值函数(TVF)映射实体。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>().ToSqlQuery(@"SELECT Id, Name, Category, BlogId FROM posts");
modelBuilder.Entity<Blog>().ToFunction("BlogsReturningFunction");
}
表值函数或TVF也可以用.NET方法映射,而不是映射到DBSet。现在,在EF Core 5.0中,当我们执行查询时,可以将一个实体映射为一个视图,如下所示:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Blog>()
.ToTable("Blogs")
.ToView("BlogsView");
}
带有属性包的共享类型实体类型
在Entity Framework Core 5.0中,我们可以定义相同的CLR类型或将其映射到多个不同的实体类型,这些类型通常被称为共享类型实体类型。当我们使用任何具有此特性的CLR类型时,NET Dictionary提供了一个特别引人注目的用例,我们可以称之为属性包。这些实体可以用于执行查询或更新操作,就像其他具有自己的专用CLR类型的普通实体类型一样。通常,只包含索引器属性的实体类型称为属性袋实体类型。这些类型的实体没有任何阴影属性。目前,只有Dictionary<string,对象>仅支持作为属性包实体类型。</string,object>它需要定义为具有唯一名称的共享实体类型,并且必须使用Set调用实现相关的DBSet属性。
public class MyContext : DbContext
{
public DbSet<Dictionary<string, object>> Blogs => Set<Dictionary<string, object>>("Blog");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.SharedTypeEntity<Dictionary<string, object>>(
"Blog", bb =>
{
bb.Property<int>("BlogId");
bb.Property<string>("Url");
bb.Property<DateTime>("LastUpdated");
});
}
}
要求1:1依赖
实体框架核心总是允许我们建模只能出现在其他实体类型的导航属性上的实体类型。这些被称为拥有的实体类型。实体包含一个拥有的实体类型称为所有者。这在使用拥有实体时最为明显,因为所有拥有实体的列都在数据库中创建为空,即使它们在模型中按需要配置。但是现在,在EF Core 5.0中,导航到拥有的实体可以配置为必需的依赖项。这样,当我们将该实体迁移到数据库级别时,它将在数据库表中创建NOT NULL列。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Author>(b =>
{
b.OwnsOne(e => e.HomeAddress,
b =>
{
b.Property(e => e.City).IsRequired();
b.Property(e => e.Postcode).IsRequired();
});
b.Navigation(e => e.HomeAddress).IsRequired();
});
}
DBContextFactory - 在Entity Framework Core 5.0中,微软引入了AddDbContextFactory和AddPooledDbContextFactory来注册一个工厂,用于在应用程序的依赖注入(D.I.)容器中创建DbContext实例。当应用程序代码需要手动创建和释放上下文实例时,这很有用。
services.AddDbContextFactory<AuthorDbContext>(b =>
b.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=TestDB"));
此时,应用程序服务,如ASP.NET核心控制器可以被注入IDbContextFactory<tcontext>,并用于实例化上下文实例。
public class MyController
{
private readonly IDbContextFactory<SomeDbContext> _contextFactory;
public MyController(IDbContextFactory<AuthorDbContext> contextFactory)
=> _contextFactory = contextFactory;
public void DoSomeThing()
{
using (var context = _contextFactory.CreateDbContext())
{
}
}
结论
Entity Framework Core 5.0已经发布,包含了许多新特性和功能。在本文中,讨论实体框架的概述和特性。还讨论了实体框架核心的概述、它所支持的数据库提供程序,以及EF Core 5.0中关键的新发布功能。
本文暂时没有评论,来添加一个吧(●'◡'●)