我正在使用 VS2013、EF6.02 和 Web API 2 开发应用程序。我正在使用 ASP.NET SPA 模板,并针对由 sql server 支持的 Entity Framework 数据源创建 RESTful api。 (在开发中,这驻留在 SQL Server 本地实例上。)
到目前为止,我有两个 API 方法(一个只读取数据,一个写入数据),我正在通过在 javascript 中调用它们来测试它们。当我在我的脚本中只调用一个方法时,任何一个都可以完美地工作。但是如果我在脚本中同时调用它们(不等待任何一个的回调触发),我会在调试器中得到不好的结果和不同的异常。一些异常表明由于存在未决事务而无法完成保存。另一个异常说明了与其他线程的冲突。有时,在尝试读取结果集时,读取操作会因空指针异常而失败。
“不允许新事务,因为 session 中还有其他线程在运行。”
这让我怀疑我是否正确地为每个请求获取了一个新的 DBContext。我的代码如下:
static Startup()
{
context = new Data.SqlServer.AppDbContext();
...
}
然后每当实例化一个工作单元时,我都会访问 Startup.context。
我尝试实现工作单元模式,每个请求共享一个 UOW 对象,该对象具有一个 DBContext 对象。
我的问题:我是否有额外的责任来确保网络请求彼此“很好地配合”?我希望这是其他人已经处理过的问题。也许我看到的错误是合法的,因为如果一个用户的数据被触及,它暂时处于无效状态,如果其他请求恰好在那个时刻进入,它们确实会失败(我应该编码预期这些失败)。我想即使每个请求都有自己的 DBContext,它们仍然共享相同的底层 SQL 数据源,所以这可能会导致问题。
我可以尝试组合一个测试用例,但我会得到不同的行为,具体取决于我放置断点的位置以及我在断点上花费的时间,向我重申这是与时间相关的。
感谢任何帮助或建议... -本
最佳答案
您的问题在于设置上下文的位置。 Startup
方法用于整个应用程序启动时,因此发出的任何请求都将使用相同的上下文。这不是按请求设置,而是按应用程序设置。至于为什么会出现错误,EntityFramework NOT 是线程安全的。由于 IIS 生成许多线程来处理并发请求,因此您的单个 context
正在跨多个线程使用。
至于解决方案,你可以看看
-依赖注入(inject)框架(例如 Ninject 或 Unity )
-在您的 UnitOfWork 类中放置一个 using 语句
using(var context = new Data.SqlServer.AppDbContext()){//do stuff}
-或者,我看到有人创建一个类来获取该请求的上下文并将其存储在 HttpContext.Cache[] 元素中(使用唯一名称以便您可以轻松地在另一个类中检索它),使得这样您就可以为相同的请求重用相同的上下文。像这样:
public AppDbContext GetDbContext()
{
var httpContext = HttpContext.Current;
if (httpContext == null) return new AppDbContext();
const string contextTypeKey = "AppDbContext";
if (httpContext.Items[contextTypeKey] == null)
{
httpContext.Items.Add(contextTypeKey, new AppDbContext());
}
return httpContext.Items[contextTypeKey] as AppDbContext;
}
要使用上述方法,只需调用 var context = GetDbContext();
注意
以上方法我们都有,但这里专门针对第三种方法。它似乎适用于两个警告。首先,不要在 using 语句中使用它,因为在请求范围内它对任何其他类都不可用(你处置它)。其次,确保您对 Application_EndRequest
的调用确实确实处理了它。我们看到这些小错误在请求结束后在内存中徘徊,导致内存使用量激增。
关于Asp.net web api + Entity Framework : multiple requests cause data conflict,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20729910/