用户国营 发表于 2025-3-14 09:52:48

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

问题描述

当在 Entity Framework Core 中先删除实体、再修改其ID 、然后重新添加时发现的实体未被删除。
问题代码示例

using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;

var serviceCollection = new ServiceCollection();

serviceCollection.AddDbContext<DbContext, TestDB>(opt => opt.UseInMemoryDatabase("test"));
serviceCollection.AddScoped<PostRepo>();

var serviceProvider = serviceCollection.BuildServiceProvider();

var id = Guid.NewGuid();

using (var scope = serviceProvider.CreateScope())
{

    PostRepo repo = scope.ServiceProvider.GetRequiredService<PostRepo>();
    DbContext db = scope.ServiceProvider.GetRequiredService<DbContext>();

    repo.Save(new Post()
    {
      Id = id,
      Name = "123",
      Tags = new List<Tag>() { new Tag() { PostId = id, TagName = "Tag1" } }
    });

    db.SaveChanges();
}

using (var scope = serviceProvider.CreateScope())
{
    PostRepo repo = scope.ServiceProvider.GetRequiredService<PostRepo>();
    DbContext db = scope.ServiceProvider.GetRequiredService<DbContext>();

    var entity = repo.QueryById(id);

    repo.Save(entity);

    db.SaveChanges();
}


using (var scope = serviceProvider.CreateScope())
{
    PostRepo repo = scope.ServiceProvider.GetRequiredService<PostRepo>();
    DbContext db = scope.ServiceProvider.GetRequiredService<DbContext>();

    var entity = repo.QueryById(id);

    Console.WriteLine($"post.Tag Count {entity?.Tags.Count()}");
}

public class TestDB : DbContext
{
    public TestDB(DbContextOptions options) : base(options)
    {
    }


    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {


      base.OnModelCreating(modelBuilder);

    }

    public DbSet<Post> Posts { get; set; }
    public DbSet<Tag> Tags { get; set; }
}

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

   
    public List<Tag> Tags { get; set; }
}
public class Tag
{
    public Guid Id { get; set; }
    public Guid PostId { get; set; }
    public string TagName { get; set; }
}

public class PostRepo
{
    private readonly DbContext _db;
    public PostRepo(DbContext db)
    {
      _db = db;
    }

    public Post? QueryById(Guid id)
    {
      var post = _db.Set<Post>().Where(x => x.Id == id).FirstOrDefault();
      if (post == null)
            return null;
      var tags = _db.Set<Tag>().Where(x => x.PostId == id).ToList();

      post.Tags = tags;
      return post;
    }

    public void Save(Post entity)
    {
      var isExist = _db.Set<Post>().Where(x => x.Id == entity.Id).Any();
      if (isExist)
      {
            _db.Update(entity);
      }
      else
      {
            _db.Add(entity);
      }

      if (entity.Tags != null)
      {
            var temp = _db.Set<Tag>().Where(x => x.PostId == entity.Id).ToList();

            //clean old
            _db.RemoveRange(temp);

            // renew id
            entity.Tags.ForEach(x => { x.Id = Guid.NewGuid(); });

            // add as new ,this cause old entity state to add
            _db.AddRange(entity.Tags);
      }
    }
}问题分析

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

自行选择

[*]使用clone方案
var items =entity.Tags.Select(x=>x.Clone()).ToList().ForEach(x => { x.Id = Guid.NewGuid(); });
_db.AddRange(items);
[*]对于一样Id的使用更新
var childItems = entity.Tags;
var deleteItem = temp.ExceptBy(childItems.Select(x => x.Id), x => x.Id);
var addItem = childItems.ExceptBy(temp.Select(x => x.Id), x => x.Id);
var update = childItems.IntersectBy(temp.Select(x => x.Id), x => x.Id);


_db.RemoveRange(deleteItem);
_db.AddRange(addItem);
_db.UpdateRange(update);
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 当在 Entity Framework 中先删除实体、再修改其ID 、然后重新添加时发现的