gpt4 book ai didi

c# - 在为已保存实体的关系添加新条目时,EF Core 不检测更改

转载 作者:行者123 更新时间:2023-12-05 02:27:21 25 4
gpt4 key购买 nike

我有一个非常基本的设置来测试和理解为什么 EF Core 6 在将新项目添加到已保存实体的多端时默认情况下不保存相关实体。

  1. 有人可以向我解释一下这个设置有什么问题吗?
  2. 如何让 EF 默认检测更改?是否有任何配置可以让 EF 检测更改而无需手动设置 entry 状态?

我还遵循了 Microsoft docs 中提供的示例它给了我相同的结果(DbUpdateConcurrencyException:数据库操作预计会影响 1 行,但实际上影响了 0 行;)

using Microsoft.EntityFrameworkCore;

var db = new AuthorsDbContext();
db.Database.EnsureCreated();


var author = new Author();
author.Id = Guid.NewGuid();

db.Authors.Add(author);
db.SaveChanges();

// Not working
// var author1 = await db.Authors.FindAsync(new object?[] { author.Id });
// author1.Posts.Add(new Post() { Id = Guid.NewGuid(), Title = "test" });
// db.SaveChanges(); //-> DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s);

// Not working
// var author1 = await db.Authors.Include(a => a.Posts).FirstAsync(a => a.Id == author.Id);
// author1.Posts.Add(new Post() { Id = Guid.NewGuid(), Title = "test" });
// db.SaveChanges(); //-> DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s);

// Not working
// var author1 = await db.Authors.AsTracking().FirstAsync(a => a.Id == author.Id);
// author1.Posts.Add(new Post() { Id = Guid.NewGuid(), Title = "test" });
// db.SaveChanges(); //-> DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s);

// Not working
// var author1 = await db.Authors.Include(a=>a.Posts).AsTracking().FirstAsync(a => a.Id == author.Id);
// author1.Posts.Add(new Post() { Id = Guid.NewGuid(), Title = "test" });
// db.SaveChanges(); //-> DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s);

// this is the only way it works
// var author1 = await db.Authors.FindAsync(new object?[] { author.Id });
// var post = new Post() { Id = Guid.NewGuid(), Title = "test" };
// author1.Posts.Add(post);
// db.Entry(post).State = EntityState.Added;
// db.SaveChanges();

public class Author
{
public Guid Id { get; set; }
public List<Post> Posts { get; set; } = new();
}

public class Post
{
public Guid Id { get; set; }
public string Title { get; set; }
}

public class AuthorsDbContext : DbContext
{
public DbSet<Author> Authors { get; set; }
public DbSet<Post> Posts { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySQL("Data Source=127.0.0.1;Initial Catalog=test-db-091;User Id=root;Password=DeV12345");
}

protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Author>().HasKey(a => a.Id);
builder.Entity<Post>().HasKey(a => a.Id);

builder.Entity<Author>().HasMany(a => a.Posts).WithOne(a => a.Author).HasForeignKey(a => a.AuthorId);
builder.Entity<Post>().HasOne(a => a.Author).WithMany().HasForeignKey(a => a.AuthorId);
}
}

最佳答案

首先,发布的模型与流畅的配置不匹配,所以让我们更正一下。根据 fluent 配置,Post 具有对 Author 的引用导航属性和显式 FK 属性:

public class Post
{
//...
public Author Author { get; set; }
public Guid AuthorId { get; set; }
}

其次,这里存在关系配置错误

builder.Entity<Author>().HasMany(a => a.Posts).WithOne(a => a.Author).HasForeignKey(a => a.AuthorId);
builder.Entity<Post>().HasOne(a => a.Author).WithMany().HasForeignKey(a => a.AuthorId);

您正在两次配置同一个关系,这通常是潜在问题的根源,因为在这种情况下,第二个配置(覆盖第一个)缺少 WithMany 中的集合导航属性>,这又会导致 2 个关系和 2 个 FK。所以删除它并只留下第一个(正确的)

builder.Entity<Author>()
.HasMany(a => a.Posts)
.WithOne(a => a.Author)
.HasForeignKey(a => a.AuthorId);

并且通常总是对每个关系使用一个流畅的配置,并在它们存在时将导航属性传递给 Has/With 调用。


现在进入主题 - 更改跟踪。当您直接调用更改跟踪 API 时(Entry.StateDbContext/DbSet Add, Remove, Attach), 然后 EF 只使用你告诉它的任何内容和所提供实体的状态。然而,当某些实体未被跟踪,并且 EF 需要确定其状态(ChangeTracker.DetectChangesSaveChanges 和其他几个地方调用)时,事情就变得棘手了,因为它不知道实体是新的(因此应该添加)还是现有的并且需要更新。

在这里,他们使用了一种适用于大多数情况的简单方法。如果 PK 不是 generated value或者如果设置了 PK(具有与 CLR 默认值不同的值),则该实体被视为现有,否则

这就是这里出现问题的原因。默认情况下,Guid PK 被视为自动生成 (ValueGeneratedOnAdd),并且由于您提供了显式值,因此 Post 实例被视为 existing 并且 EF 尝试更新(而不是插入)当然会失败。

基本上有两种方法可以处理它。首先是让默认的 PK 属性配置,不要为新实体分配 PK 值(EF 会在实体进入更改跟踪器时生成并分配一个,但它也会知道它是自动生成的,实体必须是创建),即在所有“非工作”示例中,从 new Post() { ... } 语句中删除 Id = Guid.NewGuid(), ,它们应该可以正常工作。

第二种方法是(如果您想使用显式 Guid PK)保留用法不变,但更改流畅的配置以告诉 EF 这些属性不是自动生成的,即

builder.Entity<Post>().Property(e => e.Id).ValueGeneratedNever();

关于c# - 在为已保存实体的关系添加新条目时,EF Core 不检测更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73297693/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com