1

我一直在学习 WebAPI + EF5,并基于此创建了我的第一个项目。

我在数据库中有一个表,它有 ID、NAME、DESCRIPTION 列,对 NAME 列有一个约束,以确保 NAME 对于所有记录都是唯一的。

当我通过 WebAPI 项目(在 Visual Studio 的调试中运行)向数据库发布十个实体时,十个项目出现在物理数据库中 - 很酷。

如果我在 webAPI 控制器上调用 GET 并检查 dbset.Local,我会看到我发布的 10 个项目,这是完美的。

问题是,如果我在 Visual Studio 中停止 WebAPI 项目,在 SQL Management Studio 中打开数据库并删除物理行(代表我在上面发布的 10 项),然后在 Visual Studio 中重新启动 WebAPI 的调试。如果我执行 GET 请求(通过提琴手)并通过控制器的 get 方法中的断点检查 dbset,我发现这 10 个项目仍然在 dbset.Local 属性中,即使底层中没有项目D B。如果我允许 GET 有效地完成调用 dbset.ToList() 我得到一个空的结果集。

如果我然后重新发布相同的 10 个项目,这 10 个项目再次成功到达数据库,但现在 dbset.Local 在其本地集合中有 20 个项目!?

重启WebAPI项目后,为什么dbset.Local不为空,我可以停止调试去喝咖啡重启调试,dbset.Local集合里还有20项?这是我遇到的缓存问题吗?每次在 Visual Studio 中重新启动项目时,我需要在我的项目启动代码中做些什么来清除 dbset.Local 吗?

还有为什么 dbset.Local 不遵守数据库中设置的唯一名称的约束并允许重复条目进入 dbset.Local 集合,它不应该抛出错误吗?

4

1 回答 1

1

问题是,如果我在 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 集合,它不应该抛出错误吗?

实体框架不支持唯一约束(除了主键的唯一性),即它只是不知道数据库中的唯一索引。

于 2013-05-31T23:19:00.483 回答