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

psc*_*hlz 6 c# sqlite entity-framework-core

我有一个服务器客户端架构,其中服务器为客户端提供了一个 Rest API 来同步整个数据库数据。他们将其保存到本地 SQLite 数据库中。该模型位于共享项目中,有时可能会发生变化。因此,客户端需要更新其本地 SQLite 数据库架构。这当然只有在更新客户端软件后才会发生(数据库文件保持不变)。

它只是通过通常删除数据库文件然后重新创建它来实现。

_context.Database.EnsureDeleted();
_context.Database.EnsureCreated();

AttachNewDataFromServerToDatabaseContext(_context);

_context.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

Rest API 服务被实例化为单例并始终使用相同的数据库上下文对象。第一次同步工作正常。但接下来的失败:

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.
Run Code Online (Sandbox Code Playgroud)

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

我对此的看法:

  • 每次同步数据库时实例化一个新的数据上下文可以修复它,因为更改跟踪器从“零”开始。在我看来不是一个好的解决方案。
  • 如果EnsureDeleted 还“重置”更改跟踪器,或者您可以手动进行,那就太好了。

你怎么看待这件事?谢谢你的帮助!

Iva*_*oev 7

  • 每次同步数据库时实例化一个新的数据上下文可以修复它,因为更改跟踪器从“零”开始。在我看来不是一个好的解决方案。

我宁愿说这是“正确”的解决方案。Model默认情况下,上下文元数据(又名)按上下文类型缓存,数据库连接由连接池维护,并且仅在需要时才打开/关闭。所以重用上下文实例的唯一好处是避免创建多个DbSet实例。

同时,跟踪器会保留很多“实体”实例,并防止它们在SaveChanges调用后不需要任何垃圾收集。不考虑潜在的多线程访问问题。

所以恕我直言,这是要走的路 - 实例化新的上下文,用它做一些事情并处理它。

  • 如果EnsureDeleted 还“重置”更改跟踪器,或者您可以手动进行,那就太好了。

确实那会很棒。但目前EnsureDeleted,EF Core 和 EF Core都没有提供手动执行此操作的公共方法。

但是有一种内部方式,通常存在可能在未来的某些 EF Core 版本中更改的风险。添加

using Microsoft.EntityFrameworkCore.Infrastructure;
Run Code Online (Sandbox Code Playgroud)

将允许你使用这样的东西

_context.ChangeTracker.GetInfrastructure().ResetState();
Run Code Online (Sandbox Code Playgroud)

大概之前_context.Database.EnsureDeleted();。这基本上证明您确实应该使用第一个选项(新上下文)。

更新(EF Core 3.0): 即使在内部ChangeTracker也不再公开StateManager,所以这里我们需要

using Microsoft.EntityFrameworkCore.Internal;
Run Code Online (Sandbox Code Playgroud)

和分别

_context.GetDependencies().StateManager.ResetState();
Run Code Online (Sandbox Code Playgroud)

  • 这个_context.ChangeTracker.GetInfrastruct().ResetState(); 似乎不适用于 Entity Framework Core 3.0,有其他选择吗?准确地说,GetInfrastruct() 不再可用。 (2认同)
  • 请注意,EF Core 5.0 引入了 ChangeTracker.Clear() https://learn.microsoft.com/en-gb/ef/core/what-is-new/ef-core-5.0/whatsnew (2认同)

Kaa*_*yan 5

以下几行对我有用。参考: https: //github.com/dotnet/efcore/issues/6282

    context.ChangeTracker
        .Entries()
        .ToList()
        .ForEach(e => e.State = EntityState.Detached);

    context.Database.EnsureDeleted();
    context.Database.EnsureCreated();
Run Code Online (Sandbox Code Playgroud)