如何使用SQLite使用EF Core删除整个数据库并重置更改跟踪器

时间:2019-04-01 12:00:06

标签: c# sqlite entity-framework-core

我有一个服务器客户端体系结构,其中服务器为客户端提供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.

因此,尽管整个数据库都已删除,但变更跟踪器仍然知道“旧”实体。

我的想法:

  • 每次数据库同步时实例化一个新的数据上下文可能会解决它,因为更改跟踪器此后从“零”开始。在我眼中不是很好的解决方案。
  • 如果sureDeleted还“重置”变更跟踪器,或者您可以手动进行,那将很棒。

您如何看待?感谢您的帮助!

2 个答案:

答案 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();