当我们在 Entity Framework Core 中拥有主键时,我们是不是应该始终使用 .Find() 而不是 .FirstOrDefault() ?
Posted
技术标签:
【中文标题】当我们在 Entity Framework Core 中拥有主键时,我们是不是应该始终使用 .Find() 而不是 .FirstOrDefault() ?【英文标题】:Should we always use .Find() rather than .FirstOrDefault() when we have the primary key in Entity Framework Core?当我们在 Entity Framework Core 中拥有主键时,我们是否应该始终使用 .Find() 而不是 .FirstOrDefault() ? 【发布时间】:2020-03-05 12:29:24 【问题描述】:https://docs.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/details?view=aspnetcore-3.0
在 Microsoft 文档示例中,
.FirstOrDefaultAsync()
用于Detail
和Delete
GET
;
.FindAsync()
用于DeleteConfirmed
。
我想知道这是为什么?
【问题讨论】:
Performance of Find() vs. FirstOrDefault()的可能重复 但我的问题是为什么在文档示例中 .FirstOrDefaultAsync() 用于 Detail 而 Delete GET .FindAsync() 用于 DeleteConfirmed @Areks EF Core 和 LINQ to Objects 没有任何关系。 【参考方案1】:根据reference source DbSet.Find,如果在 DbContext 中已经获取了具有相同 keyValues 的对象,则不会访问数据库:
/// Finds an entity with the given primary key values.
/// If an entity with the given primary key values exists in the context, then it is
/// returned immediately without making a request to the store.
public abstract object Find(params object[] keyValues);
FirstOrDefault,类似的函数会调用IQueryable.GetEnumerator()
,它会向IQueryable询问ProviderIQueryable.GetProvider()
的接口,然后调用IQueryProvider.Execute(Expression)
来获取Expression定义的数据。
这将始终访问数据库。
假设你有学校和他们的学生,一个简单的一对多关系。您还有一个更改学生数据的程序。
Student ChangeAddress(dbContext, int studentId, Address address);
Student ChangeSchool(dbContext, int studentId, int SchoolId);
您在程序中有这个,因为这些程序会检查更改的有效性,可能伊顿学生不允许住在牛津校区,并且可能有些学校只允许特定年龄的学生。
您有以下代码使用这些过程:
void ChangeStudent(int studentId, Address address, int schoolId)
using (var dbContext = new SchoolDbContext())
ChangeAddress(dbContext, studentId, address);
ChangeSchool(dbContext, studentId, schoolId);
dbContext.SaveChanges();
如果 Change... 函数将使用 FirstOrDefault()
,那么您将丢失其他过程所做的更改。
但是,有时您希望能够重新获取数据库数据,例如,因为其他人可能已经更改了数据,或者您刚刚所做的某些更改无效
Student student = dbContext.Students.Find(10);
// let user change student attributes
...
bool changesAccepted = AskIfChangesOk();
if (!changesAccepted)
// Refetch the student.
// can't use Find, because that would give the changed Student
student = dbContext.Students.Where(s => s.Id == 10).FirstOrDefault();
// now use the refetched Student with the original data
【讨论】:
嗨。谢谢你回答我的问题。我仍然有点想知道这是否意味着如果我使用FirstOrDefault()
,它将覆盖其他程序之前所做的所有更改,这些更改在FirstOrDefault()
之前发送到数据库?在多个用户同时进行更改的情况下,何时是使用Find()
以使其仍能获得性能并确保 CRUD 仍按预期执行的最佳时间?
或者换句话说,如果我使用.Find()
而不是.FirstOrDefault()
,会有什么不好的副作用吗?
如果您使用 Find,如果您的 dbContext 对象已经获取它,您可能会获得数据库值,也可能不会。由于这种不确定性,我很少使用 Find。如果您真的想确保获得数据库值,请使用 FirstOrDefault。【参考方案2】:
我认为是因为删除时不知道该项目是否存在,所以需要默认以防找不到。
在执行DeleteConfirmed时,您知道带有id
的项目存在并且可以使用Find
。
【讨论】:
以上是关于当我们在 Entity Framework Core 中拥有主键时,我们是不是应该始终使用 .Find() 而不是 .FirstOrDefault() ?的主要内容,如果未能解决你的问题,请参考以下文章
Entity Framework 学习系列 - 认识理解Entity Framework
Entity Framework Many to Many Relation Mapping(Entity Framework多对多关系映射)
Entity Framework技巧系列之十二 - Tip 46 - 50
Entity Framework技巧系列之十一 - Tip 42 - 45
Code First Entity Framework 6化被动为主动之explicit loading模式实战分析( 附源码)