c# - OptimisticConcurrencyException:使用共享的AppFabric缓存和相同的数据库的多个基于EF的应用程序

标签 c# entity-framework caching appfabric second-level-cache

我在与Appfabric相同的计算机上使用Web应用程序和Windows服务。
两个应用程序都重复使用基于EF(实体框架)代码优先的相同DAL代码(dll),并在Appfabric中访问相同的缓存。 Windows服务中的代码作为Quartz.Net的一部分作为Job实现。

Web应用程序必须当然支持多个请求,而Windows服务多个线程(调度程序和事件)。
对于这两者,共享的DAL dll为每个http会话创建一个DbContext对象,并为稍后的线程创建ContextID或仅为线程ContextID。 DAL使用here中的EFCachingProviders。另外,我的EF解决方案在映射中使用带有时间戳列和IsRowVersion的乐观并发。

here所述,拥有二级缓存的好处是可以跨进程访问原始状态的表示!但这似乎对我不起作用,在用例中出现如下所示的“ OptimisticConcurrencyException”:


重新启动缓存群集,重新启动Windows服务,重新启动IIS-> clean slate :)
使用Web应用程序(firefox),我将引用现有对象B插入新对象A。我可以在数据库中看到新行。一切都好。
在另一个浏览器(chrome)=新会话中使用webapp,我可以看到新对象。
接下来,Windows服务尝试执行一些后台处理并尝试更新对象B。这将导致“ OptimisticConcurrencyException”。显然,Windows服务中的进程持有版本为带日期的行版本的对象B。
如果我重新启动Windows服务,它将再次尝试相同的逻辑,并且没有异常。


因此,这两个应用程序都是多线程的,使用相同的DAL代码,连接到相同的数据库,以及相同的缓存群集和缓存。我希望更新和插入将在appfabric缓存中。我希望Windows服务的EF上下文使用最新信息。看来,这是保留旧信息的第一级缓存...
或其他问题。

请指教...

更新资料

好的,深入研究之后,我修复了Windows服务的更新问题。每个具有DAL查询的Manager对象都使用绑定到其Process ID + Thread ID的DbContext。因此,在我的Quartz Job的Execute功能中,所有(不同对象类型的)Manager都应共享由第一个Manager创建的相同DbContext。

问题是,在函数完成后,没有释放DbContext(这在基于HTTP会话的DbContext管理器中自动发生)。因此,下次执行作业时,将找到并使用相同的DbContext,到那时该DbContext已过时(旧的一级缓存???)。 2级缓存应该没有问题,因为这是共享的,应该包含最新的对象...(如果有)。

所以这部分是固定的。

新问题

因此,Web应用程序创建了一个新的对象A,更新了一个现有的对象B,Windows服务现在可以工作了,并且能够毫无问题地更新现有的(更改的)对象B。

问题:
当我刷新webapp时,它看不到对象B的更改(通过Windows服务)。

因此,如果web应用将计数更改为5,则10分钟后Windows服务更改为6,而我在相同或新的窗口/浏览器中打开web应用,我仍然看到5,而不是6!

重新启动Web应用程序(iis)没有帮助,iisreset也没有帮助。
当我执行Restart-CacheCluster ....时,它会显示6....。

因此,看起来该项目在缓存中。 Windows服务会对其进行更新,但不会使该项目无效,该项目是旧的并且由Webapp使用。

或...尽管是同一个对象,但Web应用程序在缓存中具有其自己的条目,而Win应用程序具有其自己的条目(确实无效)...。

哪一个?



我自己解决了。看起来,EF包装器使用查询字符串作为键来将项目存储在缓存中。因此,引用数据库中相同数据的2个不同查询(无论它们是否来自共享同一分布式缓存或相同应用程序的2个不同应用程序无关紧要)将具有不同的键(不同的查询字符串),因此在缓存中的位置也不同。也许它不是黑白的,而是这样的……

我不认为内部使用某种算法来检查查询是否触及现有的缓存对象。

这引起了我的问题,即我的Windows服务进行了更新,而Webapp仍然从缓存中看到了旧的更新,这只能通过执行Restart-CacheCluster命令来解决。

所以我如何解决这个问题:
我的Windows服务是一个由Quartz Scheduler触发的批处理作业。完成之后
我清除了整个缓存:

private void InvalidateCache()
    {
        try
        {
            DataCache myCache = ...
            foreach (String region in myCache.GetSystemRegions())
            {
                myCache.ClearRegion(region);
            }
        }
        catch (Exception ex)
        {
            eventLog.WriteEntry("InvalidateCache exception : " + ex.Message);
        }
    }

最佳答案

我没有答案,但是我希望下面的想法可以为您指明正确的方向。

如果这只是更新方面的问题,我将继续从数据库中读取每次更新的记录的新实例,然后进行更新。这样可以避免乐观的并发错误。请注意,DbContext不是线程安全的-我不知道这是否会导致问题,但是每次读取新内容都会解决该问题。

如果您在读取时遇到此问题,则必须跟踪各种缓存在何处以及哪个缓存未得到更新以及原因。我猜想在每个使用点都有许多用于缓存的配置选项。祝你好运.... :)

关于c# - OptimisticConcurrencyException:使用共享的AppFabric缓存和相同的数据库的多个基于EF的应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16315698/

相关文章:

c# - 将 StreamReader 返回到开头

c# - Entity Framework 的“使用”关键字与类字段

c# - 使用存储库和映射、C# 实现 MVC 设计模式

python - Django 缓存键命名

c++ - 通过良好的设计减少缓存未命中

c# - 在 C++ 中模拟 C# Random()(相同的数字)

c# - 委托(delegate) System.Action<dynamic,int> 不接受 `1' 参数

c# - ConnectRetryInterval 和 ConnectRetryCount Entity Framework SQL 连接字符串设置是否会干扰执行策略?

linux - L1 数据缓存配置

c# - 初学者使用 Linqpad 运行非常基本的 linq to sql 查询的步骤