当在 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; }

    [NotMapped]
    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导致了数据的错位。

解决方案

自行选择

  1. 使用clone方案
var items =  entity.Tags.Select(x=>x.Clone()).ToList().ForEach(x => { x.Id = Guid.NewGuid(); });
 _db.AddRange(items);
  1. 对于一样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);

来源链接:https://www.cnblogs.com/jnzhsh/p/18771782

© 版权声明
THE END
支持一下吧
点赞12 分享
37b9f1a0e2a4b355的头像-牛翰网
评论 抢沙发
头像
请文明发言!
提交
头像

昵称

取消
昵称表情代码快捷回复

    暂无评论内容