• 作者:老汪软件技巧
  • 发表时间:2024-09-28 00:03
  • 浏览量:

1、避免在循环中进行查询操作

避免在循环中进行查询操作,可以将查询结果缓存到内存中,然后对内存中的数据进行操作,可以提高性能。

这种方式适合集合数据量少的数据,否则利大于弊。

// 不建议的方式:在循环中进行查询操作
foreach (var item in itemList)
{
    var result = context.Items.FirstOrDefault(i => i.Id == item.Id);
    //执行逻辑
}
// 推荐的方式:将查询结果缓存到内存中,然后对内存中的数据进行操作
var itemIds = itemList.Select(i => i.Id).ToList();
var results = context.Items.Where(i => itemIds.Contains(i.Id)).ToList();
foreach (var item in itemList)
{
    var result = results.FirstOrDefault(r => r.Id == item.Id);
    //执行逻辑
}

2、避免使用懒加载

避免使用懒加载,因为每次访问导航属性都会触发额外的数据库查询。

使用显式加载或预先加载的技术可以提高性能。

// 不建议的方式:使用懒加载
var order = context.Orders.FirstOrDefault();
foreach (var item in order.Items)
{
  //执行逻辑
}
// 推荐的方式:使用预先加载
var order = context.Orders.Include(o => o.Items).FirstOrDefault();
foreach (var item in order.Items)
{
   //执行逻辑
}

3、合理使用Include方法

Include方法可以在一次查询中获取所有相关的实体对象。但是,当涉及到大量数据时,Include方法会导致性能下降。

可以使用手动链接查询代替Include方法。

// 不建议的方式:使用Include方法获取所有关联实体
var orders = context.Orders.Include(o => o.Items).ToList();
// 推荐的方式:使用手动链接查询代替Include方法
var orders = context.Orders
    .Join(context.OrderItems,
          o => o.Id,
          oi => oi.OrderId,
          (o, oi) => new { Order = o, OrderItem = oi })
    .ToList();

4、使用NoTracking方法

使用NoTracking方法可以避免EF Core的跟踪功能。跟踪功能在更新和删除实体对象时非常有用,但是在只需要读取数据时,跟踪功能会导致额外的开销。

使用NoTracking方法可以禁用跟踪功能,从而提高性能。

// 不建议的方式:使用默认跟踪功能
var order = context.Orders.FirstOrDefault();
// 推荐的方式:使用NoTracking方法
var order = context.Orders.AsNoTracking().FirstOrDefault();

5、执行原始SQL查询

有些情况下,使用原始的SQL语句可以比使用EF Core更高效。使用FromSqlRaw或者ExecuteSqlRaw方法可以执行原始SQL查询。

// 执行原始SQL查询
var orders = context.Orders
.FromSqlRaw("SELECT * FROM Orders WHERE Status = 'Complete'").ToList();

6、使用EF.CompileAsyncQuery

EF.CompileAsyncQuery是EF Core的一个扩展方法,它可以将LINQ表达式编译为一个异步查询。相比于动态生成LINQ查询,使用EF.CompileAsyncQuery可以提高查询性能,减少不必要的内存分配。

编译后的查询可以多次调用,而不必每次动态生成查询表达式。这样可以避免不必要的内存分配和查询优化开销,提高查询性能。

在EF Core 5.0及以上版本中,EF.CompileAsyncQuery已经成为了标准的扩展方法,无需进行任何特殊的安装或配置即可使用。它适用于查询条件固定的情况,当然也可以重新编译,不过频繁的编译会造成内存和性能的开销。

示例如下:

using Microsoft.EntityFrameworkCore.Query;
// 定义一个异步查询
private static readonly Funcint, Task> GetOrderById =
    EF.CompileAsyncQuery((MyDbContext context, int id) =>
        context.Orders.FirstOrDefaultAsync(o => o.Id == id));
// 调用异步查询
var order = await GetOrderById(context, 1);

7、AsNoTracking

在项目开发的时候,如果查询出来的对象不会被修改、删除等,那么在查询的时候,可以启用AsNoTracking,这样就能降低EF Core的资源占用

Book[] books = ctx.Books.AsNoTracking().Take(3).ToArray();
Book b1 = books[0];
b1.Title = "abc";
EntityEntry entry1 = ctx.Entry(b1);
Console.WriteLine(entry1.State);

8、通过一条SQL语句高效更新实体类

Book b1 = new Book {Id=10};
b1.Title = "yzk";
var entry1 = ctx.Entry(b1);  //直接创建实体对象
entry1.Property("Title").IsModified = true;  //标记 Title 字段被修改
Console.WriteLine(entry1.DebugView.LongView);
ctx.SaveChanges(); //只生成这个字段的更新的SQL语句

9、用一条SQL语句高效删除数据

Book b1 = new Book { Id = 28 };
ctx.Entry(b1).State = EntityState.Deleted;
ctx.SaveChanges();

10、关于 Find 和 FindAsync 方法

Find 或者 FindAsync 方法(以下简称为Find)会先在上下文查找这个对象是否已经被跟踪,如果对象已经被跟踪,就直接返回被跟踪的对象,只有在本地没有找到这个对象时,EF Core 才去数据库查询,而 Single 方法则一直都是执行一次数据库查询。

因此用 Find 方法有可能减少一次数据库查询,性能更好。但是如果在对象被跟踪之后,数据库中对应的数据已经被其他程序修改了,则 Find 方法可能会返回旧数据。

11、如何在 EF Core 中高效地删除、更新数据?12、全局查询筛选器

EF Core 支持在配置实体类的时候,为实体类设置全局查询筛选器,EF Core 会自动将全局查询筛选器应用于涉及这个实体类型的所有 LINQ 查询。这个功能常见的应用场景有 "软删除"和 "多租户"

//Fluent API配置全局查询筛选器:
builder.HasQueryFilter(b=>b.IsDeleted==false)
//忽略全局查询筛选器
ct x.Books.IgnoreQueryFilters().Where(b => b.Title.Contains("o")).ToArray();

总结

本文介绍了12种性能调优的方法,帮助我们更好地优化Entity Framework Core(EF Core)的应用表现。掌握这些技巧不仅能提升日常开发效率,在面试场合也能展示你的技术实力。当面试官询问如何提高EF Core性能时,可以从容应对,分享其中几个技巧。

需要注意的是,文中提到的优化方法并非在所有情况下都适用。例如,Include 方法在处理少量数据时非常有效,但在面对大规模数据集时可能会产生相反的效果。因此,在实际应用中需根据具体情况灵活运用。

如果您还有其他的EF Core性能优化技巧,或是对本文有任何评论或建议,欢迎留言交流!

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!