问题是,如果我在 Visual Studio 中停止 WebAPI 项目......
如果您在 ASP.NET 中托管了 WebAPI(Visual Studio 中的标准 WebAPI 项目将创建这样的项目类型),停止调试器实际上不会停止运行您的应用程序的进程,但它只会将调试器与该应用程序分离。正在运行的进程是 - 例如,如果您使用 IIS Express 作为开发测试服务器 - Web 服务器的工作进程,它将保持活动状态。(它通常在 Windows 屏幕右下角的系统托盘中有一个小图标。您可以在那里停止该进程,然后该进程被“真正”杀死。)
...在 SQL Management Studio 中打开数据库并删除物理行(代表我在上面发布的 10 个项目)...
DbSet<T>.Local
是内存中的一个集合。它没有“看到”您在 EF 上下文之外的另一个进程 (SSMS) 中从数据库中删除了行。
...然后在 Visual Studio 中重新启动 webAPI 的调试。
如果提到的工作进程仍在运行,则重新启动调试器不会启动新进程,而是将调试器附加到现有进程。这个过程中的一切都还在。
如果我执行 GET 请求(通过提琴手)并通过控制器的 get 方法中的断点检查 dbset,我发现这 10 个项目仍然在 dbset.Local 属性中,即使底层中没有项目D B。
因为检查、迭代DbSet<T>.Local
或调用DbSet<T>.Local.ToList()
不会运行数据库查询,所以它不会注意到数据库表是空的。它只是返回内存中的旧数据。
如果我允许 GET 有效地完成调用 dbset.ToList() 我得到一个空的结果集。
因为DbSet<T>.ToList()
实际上确实运行了一个数据库查询,它会发现该表是空的并将结果返回给您。
如果我然后重新发布相同的 10 个项目,这 10 个项目再次成功到达数据库,但现在 dbset.Local 在其本地集合中有 20 个项目!?
因为DbSet<T>.Local
记忆中还有旧的结果。尽管您之前DbSet<T>.ToList()
对数据库的查询返回了一个空列表以提供正确的查询结果,但这并不意味着它清除了Local
集合。如果它在数据库中找到了一个新实体(具有新的键值),则该实体将被添加到Local
集合中(然后显示 11 个项目)。但是查询永远不会从Local
.
重启WebAPI项目后,为什么dbset.Local不为空,我可以停止调试去喝杯咖啡重启调试,dbset.Local集合里还有20项?
尝试 10 杯咖啡 :) 工作进程可能会在较长时间不活动后自动关闭。我不确定它需要多长时间以及是否会在 IIS Express 上发生。
这是我遇到的缓存问题吗?每次在 Visual Studio 中重新启动项目时,我需要在我的项目启动代码中做些什么来清除 dbset.Local 吗?
是的,这(可能)是一个非常大的问题,因为缓存在Local
集合中的那些实体表明您正在跨多个 Web 请求重用相同的上下文实例。当您依赖时,这可能会导致错误的结果Local
以及许多其他问题和异常(例如,当您附加具有相同键的实体时,例如已经在 中的实体Local
等)。通常是最佳实践,建议在 Web 请求开始时创建一个新的上下文实例,在处理该单个请求期间使用它,然后在请求结束时将其释放。释放上下文将释放对象以进行垃圾收集。这Local
下一个请求时,新上下文实例的集合将为空。(这以及如何做到这一点(每个控制器,手动,依赖注入等)本身就是一个特殊主题。尝试在谷歌上搜索“每个请求的实体框架上下文”或类似的关键字来开始。)
还有为什么 dbset.Local 不遵守数据库中设置的唯一名称的约束并允许重复条目进入 dbset.Local 集合,它不应该抛出错误吗?
实体框架不支持唯一约束(除了主键的唯一性),即它只是不知道数据库中的唯一索引。