消失的EF征服之谜

时间:2015-05-28 20:06:10

标签: c# asp.net-mvc-4 iis-7 entity-framework-5

今天我收到了ASP.NET生产系统用户的紧急电话。某些用户(并非所有用户)无法输入某些数据。用户发布了数据,然后系统冻结;电话永远不会回来。

我们试图重新解决QA系统上的问题(生产数据的新恢复),但不能。然后我从我的开发环境运行并直接连接到生产数据库,伪装成受影响的用户之一。再说一遍,没问题。结论:生产环境中必定存在某种问题,可能是IIS进程中托管网站的某个地方。

所以我在生产服务器上启动了Visual Studio,并附加到IIS进程(孩子们,不要在家里做这个!),在违规代码中设置一个断点,以用户身份登录,并尝试保存数据。点击断点并逐行走,直到我按下这样的代码行:

try
{
  ...
  using (var db = new MyDataContext())
  {
    ...
    var fooToUpdate = db.Foos.Single(f => f.ID == fooId); // <-- THIS LINE
    ...
  }
}
catch (Exception ex)
{
  // some error logging
}

击中&#34;步骤&#34;在那条线上,线程简直消失了。消失得无影无踪。我在数据库上放了一个嗅探器,没有触发查询;不用说,没有涉及数据库锁定。没有例外被抛出。代码进入了实体框架,从未离开过。

数据的方式是每个用户每天都有不同且唯一的fooId,因此其他用户不会拥有相同的fooId。大多数用户都可以加载他们的Foo,但是少数几个用户一直无法加载他们的个人Foo。我尝试运行查询以在SSMS窗口中加载Foo;没有麻烦。它失败的唯一时间是在生产服务器上的这个特定的IIS进程中。

现在,我可以回收应用程序池或重新启动IIS,这可能会解决问题。但是类似的事情发生在一周之前,我们也无法跟踪它。所以我们重置IIS然后,希望问题会消失。它确实持续了一个星期。现在又回来了。

有没有人有任何想法如何让线程像这样简单地蒸发? Norman Bates躲在EF门后面吗?

2 个答案:

答案 0 :(得分:1)

鉴于线程没有神奇地蒸发,我们可以推测一些更可能的选择:

  1. 调试器很难跟踪在发布模式下编译的生产代码。仅仅因为调试版本代码在90%的时间内都有效,所以不要认为它是可靠的。优化的代码可以非常快速地使调试器脱离实际执行的轨道。当发生这种情况时,看起来线程就会消失。
  2. 假设线程合法地进入了呼叫并且没有返回(这似乎得到了应用程序的原始投诉支持&#34;冻结&#34;),那么最可能的情况是某种类型的死锁。 EntityFramework死锁并不常见,但两者都不是闻所未闻。我所知道的最常见问题通常涉及TransactionScopeCommitableTransaction。您是否在省略的代码部分中使用任何事务?

答案 1 :(得分:0)

事实证明,EF部分毕竟是红鲱鱼。我去了下载Telerik的JustDecompile和JustCode,希望能够进入EF代码,但是当我走进那条线时,我发现自己不在Single()扩展方法中,而是在其中一个我自己的方法调用 - 我想到我在前一行执行过。显然,代码与生产中的版本并不完全同步。

  

第1课:如果您附加到某个流程,那么您的执行点可能不在您认为的位置,如果您的代码与代码不同   编入该过程。

所以无论如何,既然我可以在不反编译的情况下进入代码,我注意到的第一件事是:

lock (_lockObj)
{
  ...
}

当我试图进入它时,它就冻结了。吸烟枪。

所以在某个地方,其他一些线程正在锁定这个对象。查看调用锁的其他位置,导致依赖关系的意大利面,以及另一个代码锁定的段,具有多个DB调用甚至是事务边界。它可能是代码锁定/数据库事务死锁,但是对数据库事务中的代码的简短扫描未能在事务的生命周期内获取任何其他竞争者以阻止其他任何事情。此外,有证据表明数据库没有显示任何阻止或开放交易。相反,它可能只是几百个长时间运行的进程排队的事实,所有内部代码锁都在代码锁内,最后它们在周五17:05看起来像西侧高速公路,一辆折叠式拖车卡车横跨3车道,靠近GW桥。

  

第2课:代码锁是危险的,不仅是 - 尤其是 - 与DB事务一起使用时。尝试在不使用代码锁的情况下找到使代码线程安全的方法。如果你真的必须使用代码锁,请确保尽快进出。不要给你的主题一本杂志,因为它占据了唯一的摊位,可以这么说。