我有一个服务器客户端体系结构,其中服务器为客户端提供Rest API,以同步整个数据库数据。他们将其保存到本地SQLite数据库。该模型位于共享项目中,有时可能会更改。因此,客户端需要更新其本地SQLite数据库架构。当然,这只有在更新客户端软件(数据库文件保持不变)之后才会发生。
通常只需删除数据库文件然后再重新创建即可。
_context.Database.EnsureDeleted();
_context.Database.EnsureCreated();
AttachNewDataFromServerToDatabaseContext(_context);
_context.SaveChanges();
Rest API服务被实例化为singelton,并始终使用相同的数据库上下文对象。第一次同步工作正常。但是接下来的失败:
System.InvalidOperationException: The instance of entity type '***' cannot be tracked because another instance with the key value '{id: ***}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
因此,尽管整个数据库都已删除,但变更跟踪器仍然知道“旧”实体。
我的想法:
您如何看待?感谢您的帮助!
答案 0 :(得分:2)
- 每次数据库同步时实例化一个新的数据上下文可能会解决它,因为更改跟踪器此后从“零”开始。在我眼中不是很好的解决方案。
我宁愿说这是“正确”的解决方案。默认情况下,上下文元数据(也称为Model
)按上下文类型进行缓存,数据库连接由连接池维护,并且仅在需要时才打开/关闭。因此,重用上下文实例的唯一好处是避免创建多个DbSet
实例。
与此同时,跟踪器将保留大量“实体”实例,并防止它们在SaveChanges
调用后不需要任何垃圾回收。不计算潜在的多线程访问问题。
所以恕我直言,这是要走的路-实例化新上下文,对其进行处理并处置。
- 如果sureDeleted还“重置”变更跟踪器,或者您可以手动进行,那将很棒。
的确如此。但是目前EnsureDeleted
并没有这样做,EF Core也没有提供 public 手动方式。
不过,这是一种内部方式,通常会在某些将来的EF Core版本中进行更改。添加
using Microsoft.EntityFrameworkCore.Infrastructure;
将允许您使用类似的内容
_context.ChangeTracker.GetInfrastructure().ResetState();
可能在_context.Database.EnsureDeleted();
之前。基本上证明了您应该真正使用第一个选项(新上下文)。
答案 1 :(得分:2)
以下几行对我有用。参考:https://github.com/dotnet/efcore/issues/6282
context.ChangeTracker
.Entries()
.ToList()
.ForEach(e => e.State = EntityState.Detached);
context.Database.EnsureDeleted();
context.Database.EnsureCreated();