修改后的本地 EF Core 实体仍由新查询获取

Modified local EF Core Entity still fetched by a new query

让我们考虑这两个实体 类:

public class Student
{
    public int Id { get; set; }
    
    public int? TeacherId { get; set; }
    
    public virtual Teacher { get; set; }
}

public class Teacher
{
    public int Id { get; set; }
    
    public virtual ICollection<Student> Students { get; set; }
}

让我们考虑这个 Entity Framework Core 场景:

var studentSet = dbContext.Set<Student>();

// Get a student
var specialStudent = await studentSet.FindAsync(42);

// Exclude it from the relationship
specialStudent.TeacherId = null;

// Get all the remaining student in the relation ship
var assignedStudents = await studentSet.Where(s => s.TeacherId != null).ToListAsync();

// Do some modifications on assigned students

// Only at the very end of the scenario: commit all the changes to the database in one call
await dbContext.SaveChangesAsync();

我的问题是我的 specialStudentassignedStudents 的一部分,即使它的 TeacherId 正如预期的那样为空。

我猜 Entity Framework Core 会 运行 查询数据库,将结果与其缓存的实体进行比较,获取丢失的实体,然后重新 运行查询以从结果中排除不适合查询的已修改实体。

我想我最后的猜测是错误的。

是否可以达到我想要的效果?

我不知道它是否与我在 3.1.4 版本中使用 Npgsql.EntityFrameworkCore.PostgreSQL 相关,并且我的 DbContext 配置为使用代理 (optionsBuilder.UseLazyLoadingProxies())。

在其他情况下,为了使用您的算法,您使用了事务(仅当您需要将 TeacherId 分配给 null 并同时修改其他学生时)并在每个步骤后调用 SaveChanges。此外,您必须标记 EntityState.Modified 每条修改记录(或 属性),否则将不会保存到数据库。

为什么不用这个?

var assignedStudents = await dbContext.Students.Where(s => s.Id !=42 ) .ToListAsync();
// Do some modifications on assigned students
var specialStudent =  await dbContext.Students.Where(s => s.Id ==42 ) 
specialStudent.TeacherId = null;
dbContext.Entry(specialStudent).State = EntityState.Modified;
await dbContext.SaveChangesAsync()

我不明白为什么 Panagiotis Kanavos 在评论中说我不应该这样做。交易就是为了这个目的而存在的,Entity Framework 让我们可以很容易地使用它。我不明白为什么会 break EF's way of working.

这就是为什么我不会立即将其标记为已接受的答案,因此 he/someone else 可以解释为什么它不好。但与此同时,这对我有用:

// Initialize the transaction
await dDbContext.Database.BeginTransactionAsync();

var studentSet = dbContext.Set<Student>();

// Get a student
var specialStudent = await studentSet.FindAsync(42);

// Exclude it from the relationship
specialStudent.TeacherId = null;

// Apply the changes locally so it can be reflected in the next calls 
// Since there is a transaction "in progress", the changes are not commited to the database
await dbContext.SaveChangesAsync();

// Get all the remaining student in the relationship
// This time, mySpecialStudent is no longer part of the results
var assignedStudents = await studentSet.Where(s => s.TeacherId != null).ToListAsync();

// Do some modifications on assigned students

// Save the changes to thoses entities
await dbContext.SaveChangesAsync();

// Only at the very end of the scenario commit the transaction to push the data to the database
await dbContext.Database.CurrentTransaction.CommitAsync();