efcore的一些性能优化

文章介绍了EFCore中的AsNoTracking用于禁用实体跟踪以提升性能,Include用于一次性加载关联数据,以及Any(),Count(),FirstOrDefault()在查询性能上的差异。同时强调了根据需求选择Find()和FirstOrDefault()的方法。
摘要由CSDN通过智能技术生成

一、AsNoTracking

EF Core 提供了一种称为自动跟踪的功能,它可以将查询到的实体跟踪在内存中,以便对它们进行更改直到提交到数据库。在查询实体时,EF Core 将引用添加到内部跟踪集合中,以便可以在修改时进行检测和持久化到数据库中。

using (var context = new MyContext())
{
    var blog = context.Blogs.First();
    blog.Url = "https://example.com/";
    context.SaveChanges();
}

在上述示例中,我们在从数据库中查询到 Blog 实体后,更改了它的 Url 属性。由于默认情况下 EF Core 启用了自动跟踪的功能,该更改将自动将被检测到,然后 EF Core 将调用 SaveChanges() 方法将更改提交到数据库中。

需要注意的是,在大型应用程序或查询中使用自动跟踪可能会导致性能问题,因此对于一些查询不需要用到自动跟踪的情况,我们可以通过手动禁用跟踪来优化查询性能。可以通过使用 .AsNoTracking() 方法来禁用自动跟踪的功能,这样查询到的实体将不会被跟踪。例如:

using (var context = new MyContext())
{
    var blogs = context.Blogs.AsNoTracking().ToList();
}

总之,自动跟踪是 EF Core 中一个非常有用的功能,使得对实体的更改能够被跟踪和持久化到数据库中。但是,在大型的查询和应用程序中,需要使用其它优化方法来提高性能,例如使用手动禁用跟踪和 LINQ 查询优化等技巧。

 

二、Include

当使用Include() 方法时,EF Core通过一条SQL 查询同时加载主实体和相关联实体,从而减少了数据库查询的次数。在查询时,EF Core 查询生成器会在LINQ查询时自动将包含Include() 方法的 Lambda 表达式转换为联接查询,从而将实体及其关联数据加载到内存中。

例如,以下代码可用于将文章及其相关作者信息加载到内存中:

var blogs = context.Blogs.Include(b => b.Author).ToList();

上述代码是通过使用 Include() 方法来加载相关的实体,从而避免了额外的数据库查询。

当我们查询 Blog 实体时,EF Core 会尝试将关联的 Author 实体也加载到内存中,这样我们就能够在一次查询中获得Blog及其相关的Author信息。在查询时,EF Core查询生成器会自动将包含Include()方法的Lambda表达式转换成一个或多个联接查询,以便将实体及其相关数据加载到内存中。

需要注意的是,如果实体对象非常复杂,包含大量的关系数据,可能会导致性能问题。在使用Include()时,请根据需要仅加载必要的数据。对于多对多关系的实体,可以使用以下方式进行贪婪加载:

context.Blogs.Include(blog => blog.Tags).ThenInclude(tag => tag.Posts)

上述代码会同时将标签和它们关联的文章数据在一次查询中都加载到内存中。

总之,Include() 是一个有用的 EF Core 查询方法,能够帮助我们减少数据库查询次数,提高应用程序性能。同时,我们需要注意贪婪加载(Eager Loading)的使用,避免加载不必要的数据,从而影响查询效率。

三、Any()

在 EF Core 中,使用 .Any(),.Count(),.FirstOrDefault() 都可以得出相同的结果,但是它们在 SQL 查询中的表现有所不同,因此性能也是不同的。

  1. .Any()

.Any() 方法只是检查是否存在符合给定条件的数据记录。当使用 .Any() 时,EF Core 会在数据库中生成一条 “EXISTS” 子句。这比执行 COUNT() 检查数据记录数更快,因为 EXISTS 子句只返回 true 或 false。在执行 COUNT() 时,查询需要检查每个记录,直到计算出总记录数。因此,使用 .Any() 通常比 .Count() 更快。

例如:

var hasBlogs = context.Blogs.Any();

上述代码使用 .Any() 方法来检查 Blogs 表中是否存在记录。EF Core 将生成以下类似的 SQL 语句:

SELECT CASE WHEN EXISTS(SELECT 1 from [Blogs]) THEN 1 ELSE 0 END
  1. .Count()

.Count() 方法计算符合给定条件的数据记录的总数。当使用 .Count() 时,EF Core 会在数据库中生成一条 “SELECT COUNT(*)” 子句。因为要计算总记录数,所以使用 .Count() 比 .Any() 更耗费资源,因此执行时更慢。

例如:

var countBlogs = context.Blogs.Count();

上述代码使用 .Count() 方法来获取 Blogs 表中的记录总数。EF Core 将生成以下类似的 SQL 语句:

SELECT COUNT(*) from [Blogs]
  1. .FirstOrDefault()

.FirstOrDefault() 方法返回符合给定条件的第一条或默认数据记录。当使用 .FirstOrDefault() 时,EF Core 会在数据库中生成一条 SELECT 查询语句,并在匹配数据记录时停止查询。因此,.FirstOrDefault() 的效率通常比 .Count() 更高,但比 .Any() 稍微慢一些。

例如:

var blog = context.Blogs.FirstOrDefault();

上述代码使用 .FirstOrDefault() 方法来获取 Blogs 表的第一条数据记录。EF Core 将生成以下类似的 SQL 语句:

SELECT TOP(1) * FROM [Blogs]

总之,.Any() 通常比 .Count() 更快,.FirstOrDefault() 的效率通常介于两者之间。当需要获取查询结果集合的详细信息时,可以使用 .Count(),当只需要知道结果集是否存在时,可以使用 .Any()。而 .FirstOrDefault() 通常用于获取数据集合中的第一个记录。

四、Find()

在 EF Core 中,Find() 和 FirstOrDefault() 方法都是用于从数据库中获取指定实体对象的方法,但它们的使用场景有所不同。

Find() 方法用于在指定的 DbSet<TEntity> 集合中查找实体,并根据主键的值将该实体直接从内存中获取。如果在内存中找不到该实体,则会从数据库中获取它。而 FirstOrDefault() 方法则在数据库中执行一条 SQL 查询,用于从集合中获取第一条记录或匹配指定条件的第一条记录。

因此,当要查询的实体对象已经存在于 DbContext 的 ChangeTracker 中时(通常是在前面的查询中读取过该实体数据,或者手工将该实体添加到 DbContext 的 ChangeTracker 中),使用 Find() 方法来获取数据的效率比使用 FirstOrDefault() 方法更高,因为它不需要额外的数据库查询操作。

而如果要在集合中查找符合指定条件的第一条记录,或者要返回数据集合中的第一条记录,则应该使用 FirstOrDefault() 方法。

需要注意的是,Find() 方法仅适用于透明的主键属性。对于复合主键、非透明主键、已定义计算属性或属性的值生成,不能使用 Find() 方法。

总之,Find() 和 FirstOrDefault() 方法的使用场景有所不同,根据场景选择合适的方法可以提高 EF Core 应用程序的性能。如果要查询的实体已经存在于 DbContext 的 ChangeTracker 中时,使用 Find() 方法的效率更高。否则,应该使用 FirstOrDefault() 方法进行查询。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值