当在 Entity Framework 中先删除实体、再修改其ID 、然后重新添加时发现的 ...

打印 上一主题 下一主题

主题 942|帖子 942|积分 2826

问题描述

当在 Entity Framework Core 中先删除实体、再修改其ID 、然后重新添加时发现的实体未被删除。
问题代码示例
  1. using MediatR;
  2. using Microsoft.EntityFrameworkCore;
  3. using Microsoft.Extensions.DependencyInjection;
  4. using System.ComponentModel.DataAnnotations.Schema;
  5. using System.Diagnostics;
  6. var serviceCollection = new ServiceCollection();
  7. serviceCollection.AddDbContext<DbContext, TestDB>(opt => opt.UseInMemoryDatabase("test"));
  8. serviceCollection.AddScoped<PostRepo>();
  9. var serviceProvider = serviceCollection.BuildServiceProvider();
  10. var id = Guid.NewGuid();
  11. using (var scope = serviceProvider.CreateScope())
  12. {
  13.     PostRepo repo = scope.ServiceProvider.GetRequiredService<PostRepo>();
  14.     DbContext db = scope.ServiceProvider.GetRequiredService<DbContext>();
  15.     repo.Save(new Post()
  16.     {
  17.         Id = id,
  18.         Name = "123",
  19.         Tags = new List<Tag>() { new Tag() { PostId = id, TagName = "Tag1" } }
  20.     });
  21.     db.SaveChanges();
  22. }
  23. using (var scope = serviceProvider.CreateScope())
  24. {
  25.     PostRepo repo = scope.ServiceProvider.GetRequiredService<PostRepo>();
  26.     DbContext db = scope.ServiceProvider.GetRequiredService<DbContext>();
  27.     var entity = repo.QueryById(id);
  28.     repo.Save(entity);
  29.     db.SaveChanges();
  30. }
  31. using (var scope = serviceProvider.CreateScope())
  32. {
  33.     PostRepo repo = scope.ServiceProvider.GetRequiredService<PostRepo>();
  34.     DbContext db = scope.ServiceProvider.GetRequiredService<DbContext>();
  35.     var entity = repo.QueryById(id);
  36.     Console.WriteLine($"post.Tag Count {entity?.Tags.Count()}");
  37. }
  38. public class TestDB : DbContext
  39. {
  40.     public TestDB(DbContextOptions options) : base(options)
  41.     {
  42.     }
  43.     protected override void OnModelCreating(ModelBuilder modelBuilder)
  44.     {
  45.         base.OnModelCreating(modelBuilder);
  46.     }
  47.     public DbSet<Post> Posts { get; set; }
  48.     public DbSet<Tag> Tags { get; set; }
  49. }
  50. public class Post
  51. {
  52.     public Guid Id { get; set; }
  53.     public string Name { get; set; }
  54.     [NotMapped]
  55.     public List<Tag> Tags { get; set; }
  56. }
  57. public class Tag
  58. {
  59.     public Guid Id { get; set; }
  60.     public Guid PostId { get; set; }
  61.     public string TagName { get; set; }
  62. }
  63. public class PostRepo
  64. {
  65.     private readonly DbContext _db;
  66.     public PostRepo(DbContext db)
  67.     {
  68.         _db = db;
  69.     }
  70.     public Post? QueryById(Guid id)
  71.     {
  72.         var post = _db.Set<Post>().Where(x => x.Id == id).FirstOrDefault();
  73.         if (post == null)
  74.             return null;
  75.         var tags = _db.Set<Tag>().Where(x => x.PostId == id).ToList();
  76.         post.Tags = tags;
  77.         return post;
  78.     }
  79.     public void Save(Post entity)
  80.     {
  81.         var isExist = _db.Set<Post>().Where(x => x.Id == entity.Id).Any();
  82.         if (isExist)
  83.         {
  84.             _db.Update(entity);
  85.         }
  86.         else
  87.         {
  88.             _db.Add(entity);
  89.         }
  90.         if (entity.Tags != null)
  91.         {
  92.             var temp = _db.Set<Tag>().Where(x => x.PostId == entity.Id).ToList();
  93.             //clean old
  94.             _db.RemoveRange(temp);
  95.             // renew id
  96.             entity.Tags.ForEach(x => { x.Id = Guid.NewGuid(); });
  97.             // add as new ,this cause old entity state to add
  98.             _db.AddRange(entity.Tags);
  99.         }
  100.     }
  101. }
复制代码
问题分析

当我第一次调用RemoveRange时,ChangeTracker 将 tag 标志为 Delete
ChangeTracker的Debug View 如下所示
  1. Tag {Id: 3cb7d0a8-e044-4818-9afd-4f09c20ae60a} Deleted
复制代码
当执行完 entity.Tags.ForEach(x=>{ x.Id =  Guid.NewGuid();}); 后
ChangeTracker的Debug View 如下所示
  1. Tag {Id: ac9d9fc7-5a8d-4b2e-9ac7-ca1a2778af8d} Deleted
复制代码
可以看到 Tag 的Id 产生了变革,这说明 ChangeTracker 追踪的是对象,自此tag的Id产生了变革,便无法删除了对于Id的数据了
当执行后续的AddRanage时,tag就酿成了Add
ChangeTracker的Debug View 如下所示
  1. Tag {Id: ac9d9fc7-5a8d-4b2e-9ac7-ca1a2778af8d} Added
复制代码
所以就导致了原先想要删除Tag的删除信息丢失。
根本原因: 就是 add和remove的都是同一个对象的引用,变动Id导致了数据的错位。
办理方案

自行选择

  • 使用clone方案
  1. var items =  entity.Tags.Select(x=>x.Clone()).ToList().ForEach(x => { x.Id = Guid.NewGuid(); });
  2. _db.AddRange(items);
复制代码

  • 对于一样Id的使用更新
  1. var childItems = entity.Tags;
  2. var deleteItem = temp.ExceptBy(childItems.Select(x => x.Id), x => x.Id);
  3. var addItem = childItems.ExceptBy(temp.Select(x => x.Id), x => x.Id);
  4. var update = childItems.IntersectBy(temp.Select(x => x.Id), x => x.Id);
  5. _db.RemoveRange(deleteItem);
  6. _db.AddRange(addItem);
  7. _db.UpdateRange(update);
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

用户国营

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表