当在 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]