EFCore相关知识分享
文章目录
- EFCore相关知识分享
- 前言
- [EFCore 的优势](#EFCore 的优势)
- SaveChang的作用
- [EF Core 查询优化小技巧](#EF Core 查询优化小技巧)
-
- 使用投影(投影到所需的数据类型)
- [延迟加载(Lazy Loading)与显式加载(Eager Loading)](#延迟加载(Lazy Loading)与显式加载(Eager Loading))
- [使用 AsNoTracking](#使用 AsNoTracking)
- [减少客户端评估(Client Evaluation)](#减少客户端评估(Client Evaluation))
- 使用合适的索引
- [避免 Select * 和不必要的 Include](#避免 Select * 和不必要的 Include)
- [使用异步查询(Async Queries)](#使用异步查询(Async Queries))
- [分析和优化生成的 SQL 查询](#分析和优化生成的 SQL 查询)
- 使用批量操作(Batching)和分页(Paging)
- [EF Core中entitystate的作用](#EF Core中entitystate的作用)
- [EF Core中引用属性和导航属性](#EF Core中引用属性和导航属性)
- [EF Core中如何通过数据生成实体和dbcontext?](#EF Core中如何通过数据生成实体和dbcontext?)
- [EF Core如何处理并发?](#EF Core如何处理并发?)
- [EF Core实现事务的几种方式](#EF Core实现事务的几种方式)
- 总结
前言
EF Core相比大家都听说过,它是 .NET Core 中的一个开源对象关系映射(ORM)框架。它允许开发者以面向对象的方式与数据库进行交互,从而简化了数据库访问的复杂性。EF Core 是 Entity Framework 的跨平台版本,支持 .NET Core 以及 .NET Framework。
EFCore 的优势
- 提高开发效率:EF Core 使得开发者可以使用 C# 代码表达数据操作,无需编写复杂的 SQL 语句。通过定义实体类和数据库上下文,EF Core 可以自动生成相应的数据库表结构和 SQL 语句。这大大减少了开发过程中编写 SQL 语句的工作量,提高了开发效率。
- 跨平台支持:EF Core 支持多种数据库系统,如 SQL Server、MySQL、PostgreSQL、SQLite 等。这意味着开发者可以在不同的平台和数据库环境中使用 EF Core,具有很好的可移植性。
- 强大的查询功能:EF Core 提供了丰富的 LINQ 查询功能,支持复杂的查询操作。通过 LINQ,开发者可以使用 C# 代码编写查询表达式,EF Core 会将其转换为相应的 SQL 语句。这使得查询操作更加直观和灵活。
- 数据库迁移功能:EF Core 的数据库迁移功能可以帮助开发者安全地更改数据库结构。通过迁移,开发者可以在开发过程中轻松地添加、修改或删除数据库表和字段。这为数据库版本管理和持续集成提供了便利。
摘录自:itpub博客
SaveChang的作用
EF Core中的SaveChanges方法主要用于将本地缓存中的所有状态变化提交到数据库,确保数据的一致性和完整性。当调用DbContext的SaveChanges方法时,EF Core会自动检测所有跟踪的实体变化,并将这些变化应用到数据库中。如果所有操作都成功,则提交事务;如果任何操作失败,则回滚事务,确保数据库状态不变
EF Core 查询优化小技巧
使用投影(投影到所需的数据类型)
避免使用 Select(x => x) 来选择所有列,而是仅选择需要的列。这样可以减少数据库和网络传输的数据量。
csharp
var results = dbContext.Users
.Select(u => new { u.Id, u.Name })
.ToList();
延迟加载(Lazy Loading)与显式加载(Eager Loading)
1. 延迟加载:默认情况下,EF Core 会懒加载相关实体。确保只在必要时才使用它,因为过多的懒加载可能导致多次数据库查询。
2. 显式加载:使用 Include 方法显式指定需要加载的相关实体,这可以减少查询次数并提升性能。
csharp
var results = dbContext.Users
.Include(u => u.Orders)
.ToList();
使用 AsNoTracking
当你不需要跟踪实体的状态时(例如,只读操作),使用 AsNoTracking() 可以避免 EF Core 为每个实体维护其状态,从而提升性能。
csharp
var results = dbContext.Users
.AsNoTracking()
.ToList();
减少客户端评估(Client Evaluation)
确保 LINQ 查询尽可能在数据库服务器上执行,避免将数据拉回客户端再进行过滤或排序。使用 AsEnumerable() 或 AsQueryable() 可能有助于控制这一点。
csharp
var results = dbContext.Users
.AsQueryable() // 保持查询在服务器端执行
.Where(u => u.Age > 30)
.ToList();
使用合适的索引
确保数据库表有适当的索引。索引可以显著提高查询速度,特别是在执行大量数据筛选、排序或连接操作时。
避免 Select * 和不必要的 Include
只选择需要的列,并避免不必要的 Include 调用。例如,如果只需要用户的基本信息,就不要加载不必要的关联数据
csharp
var results = dbContext.Users
.Select(u => new { u.Id, u.Name }) // 选择必要的列
.ToList();
使用异步查询(Async Queries)
对于需要长时间运行或涉及大量数据的查询,使用异步方法可以避免阻塞主线程,提高应用程序的响应性。
csharp
var results = await dbContext.Users.ToListAsync();
分析和优化生成的 SQL 查询
使用 EF Core 的日志功能查看生成的 SQL 查询,并对其进行优化。可以通过设置日志级别为 Information 或 Debug 来查看 SQL 查询。
csharp
dbContext.Database.Log = message => Debug.WriteLine(message);
使用批量操作(Batching)和分页(Paging)
对于大数据集,考虑使用分页技术来减少单次查询的数据量,或者使用批量操作来减少数据库操作的次数。
csharp
var users = dbContext.Users.Skip((page - 1) * pageSize).Take(pageSize).ToList(); // 分页示例
EF Core中entitystate的作用
- 跟踪实体变化:通过EntityState,EF Core能够跟踪实体对象的状态变化,从而在调用SaveChanges方法时,能够准确地执行相应的数据库操作(如插入、更新或删除)
- 优化性能:由于EF Core只会对处于Modified或Added状态的实体执行数据库操作,因此通过合理设置EntityState,可以减少不必要的数据库交互,显著提升系统性能
- 开发简化:开发者可以通过EntityState清晰地理解与数据库的交互关系,这不仅降低了出错概率,也提高了开发效率
EntityState的五种状态及其定义如下:
Detached(游离状态):实体对象与EF Core的上下文没有任何关系,EF Core不会跟踪它的变化。
Unchanged(未更改状态):实体对象被加载到EF Core的上下文中,且自加载后没有发生任何变化。
Deleted(已删除状态):实体对象在数据库中存在,但已经被标记为删除。
Modified(已修改状态):实体对象在数据库中存在,并且某些属性值已经被修改。
Added(已添加状态):实体对象已经被添加到EF Core的上下文中,但还没有被保存到数据库中
EF Core中引用属性和导航属性
引用属性
引用属性通常用于表示一个实体与另一个实体之间的单向关系。例如,在Blog和Post的关系中,Post实体包含一个引用属性Blog,用于指向其所属的Blog实体。通过这个属性,可以直接访问相关的Blog实体,而不需要通过额外的查询操作
导航属性
导航属性用于表示实体之间的双向关系。它们不仅包含对相关实体的引用,还提供了通过这些引用访问相关实体数据的能力。导航属性可以是集合导航属性或引用导航
集合导航属性
用于表示一个实体与多个相关实体的关系。例如,在Person和CarData的关系中,Person实体包含一个集合导航属性Cars,用于存储其拥有的所有CarData实体
引用导航属性
用于表示一个实体与单个相关实体的关系。例如,在Post和Blog的关系中,Post实体包含一个引用导航属性Blog,用于指向其所属的Blog实体
EF Core中如何通过数据生成实体和dbcontext?
EF Core生成实体的方式主要有以下几种:
- 通过实体类直接实例化
- 通过查询数据库并映射到实体
- 使用 FromSql 或 FromSqlRaw 方法直接从数据库查询并映射到实体(高级用法)
- 使用 DbSet 的 Add 方法自动生成实体(例如,从用户输入或表单数据)
EF Core如何处理并发?
使用乐观并发
乐观并发假定多个事务可以同时发生,并且在提交时检查是否有冲突。在 EF Core 中,这通常通过在模型中使用 rowversion(在 SQL Server 中为 timestamp 类型)或 ConcurrencyToken 属性来实现。
使用 rowversion(timestamp)在 SQL Server 中,你可以直接使用 rowversion 类型作为并发令牌。
csharp
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public byte[] RowVersion { get; set; } // 使用 byte[] 类型对应 rowversion
}
在 EF Core 中配置模型时,不需要做额外配置,EF Core 会自动处理这些字节。使用 ConcurrencyToken;如果你使用的是其他数据库(如 PostgreSQL),你可以使用 ConcurrencyToken 属性。
csharp
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
[ConcurrencyCheck]
public int? Version { get; set; } // 使用 int? 作为令牌类型
}
异常处理与重试机制
当使用乐观并发时,如果检测到并发冲突,EF Core 会抛出一个 DbUpdateConcurrencyException 异常。你可以通过捕获这个异常并实现重试逻辑来处理这种情况。
csharp
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
// 处理冲突,例如重新加载实体并再次尝试更新或通知用户冲突发生
var entry = ex.Entries.Single();
var clientValues = entry.CurrentValues;
var databaseValues = entry.GetDatabaseValues();
if (databaseValues == null)
{
// 数据已被删除,可以选择抛出异常或进行其他处理
}
else
{
// 应用任何业务逻辑以决定如何合并数据,例如询问用户或自动合并某些字段等。
// 然后重新尝试保存更改。
entry.CurrentValues.SetValues(databaseValues); // 可以选择重新加载数据库的值或合并当前值和数据库值。
entry.State = EntityState.Modified; // 标记为修改状态以便重试。
try
{
db.SaveChanges(); // 再次尝试保存更改。
}
catch (DbUpdateConcurrencyException) // 可能需要再次捕获异常以处理可能的再次冲突。
{
// 处理再次冲突的情况。
}
}
}
使用悲观锁(悲观并发控制)
虽然 EF Core 主要支持乐观并发控制,但在某些情况下,你可能会需要使用悲观锁来控制并发。这可以通过数据库的特定功能实现,例如 SQL Server 的 SELECT ... FOR UPDATE 语句或者使用 WITH (ROWLOCK, UPDLOCK) 提示。但是,在 EF Core 中直接使用这些功能通常需要自定义 SQL 或通过存储过程来实现。例如:
csharp
var blogs = db.Blogs.FromSqlRaw("SELECT * FROM Blogs WITH (ROWLOCK, UPDLOCK) WHERE Id = {0}", id).ToList();
对于并发处理的建议
- 尽早捕获并处理异常:尽早捕获 DbUpdateConcurrencyException 并处理,避免让异常传播到用户界面层。
- 用户反馈:当检测到并发冲突时,提供清晰的反馈给用户,让他们知道发生了什么以及可能的解决方案。
- 日志记录:记录并发冲突的详细信息,以便于分析和调试。
- 测试:在开发过程中充分测试并发场景,确保你的解决方案在各种情况下都能正常工作。
EF Core实现事务的几种方式
SaveChanges
在默认情况下,EF Core的SaveChanges方法支持事务。如果在一次操作中添加或修改了多个实体,SaveChanges会自动处理事务,确保要么所有操作都成功,要么在遇到错误时回滚所有操作,从而保持数据的一致性
DbContextTransaction
可以通过调用DbContext的Database.BeginTransaction方法来手动开始一个事务。这种方法提供了更细粒度的控制,可以在需要时手动提交或回滚事务。使用Using语句可以确保事务在代码块执行完毕后自动回滚,无需手动调用回滚操作
TransactionScope
System.Transactions命名空间下的TransactionScope类提供了更高层次的事务管理。它不仅可以管理数据库事务,还可以管理其他资源的事务。TransactionScope会自动管理事务的提交和回滚,使得代码更加简洁和安全
对于事务处理的建议
- 支付转账、图书馆借书等操作需要确保数据的一致性,事务可以保证要么所有操作都成功,要么在遇到错误时全部回滚。
- 在复杂业务场景中,默认的事务可能无法满足需求,需要手动控制事务的开始、提交和回滚。
- 使用过滤器(Filters)可以简化事务的管理,通过在ASP.NET Core的Startup.cs中全局注册事务过滤器,可以自动管理所有数据库操作的事务
总结
EF Core的相关知识分享到这里就结束了,后面如果还有其他知识会更新在这篇文章中。感谢大家的观看。