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

C# OptimisticConcurrencyException:使用共享AppFabric缓存和相同数据库的多个基于EF的应用程序,c#,entity-framework,caching,appfabric,second-level-cache,C#,Entity Framework,Caching,Appfabric,Second Level Cache,我在与Appfabric相同的计算机上使用web应用程序和windows服务。 两个应用程序都重用相同的DAL代码(dll),该代码首先基于EF(实体框架)代码,并访问Appfabric中的相同缓存。windows服务中的代码作为作业实现,作为 web应用程序必须支持多个非正常请求,windows服务必须支持多个线程(调度程序和事件)。 对于这两种情况,共享DAL dll会为每个http会话和线程ContextID创建一个DbContext对象,或者仅为后者创建一个线程ContextID。DAL

我在与Appfabric相同的计算机上使用web应用程序和windows服务。 两个应用程序都重用相同的DAL代码(dll),该代码首先基于EF(实体框架)代码,并访问Appfabric中的相同缓存。windows服务中的代码作为作业实现,作为

web应用程序必须支持多个非正常请求,windows服务必须支持多个线程(调度程序和事件)。 对于这两种情况,共享DAL dll会为每个http会话和线程ContextID创建一个DbContext对象,或者仅为后者创建一个线程ContextID。DAL使用来自的EFCachingProviders。此外,我的EF解决方案在映射中使用带有时间戳列和IsRowVersion的乐观并发

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

  • 重新启动缓存群集,重新启动windows服务,重新启动iis->clean slate:)
  • 使用web app(firefox),我插入了一个新的对象a,它引用了现有的对象B。我可以看到数据库中的新行。好的
  • 在另一个浏览器中使用webapp(chrome)=新建会话,我可以看到新对象
  • 接下来,windows服务尝试进行一些后台处理并尝试更新对象B。这将导致“OptimisticConcurrencyException”。显然,windows服务中的进程正在保存对象B的一个版本,其版本已过期
  • 如果我重新启动windows服务,它会再次尝试相同的逻辑,并且毫无例外地工作
  • 所以这两个应用程序都是多线程的,使用相同的DAL代码,连接到相同的数据库,以及相同的缓存集群和相同的缓存。我希望更新和插入在appfabric缓存中。我希望windows服务的EF上下文使用最新信息。不知何故,它似乎是保存旧信息的第一级缓存。。。 或者别的什么事情出了问题

    请建议

    更新

    好的,在仔细研究之后,我修复了windows服务的更新问题。具有DAL查询的每个管理器对象都使用绑定到其进程ID+线程ID的DbContext。因此,在my Quartz作业的执行函数中,所有管理器(不同对象类型)都应共享由第一个管理器创建的同一DbContext

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

    所以这部分是固定的

    新问题

    因此,web应用程序将创建一个新对象a,更新现有对象B,windows服务现在可以正常工作,并且能够毫无问题地更新现有(更改的)对象B

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

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

    重新启动webapp(iis)没有帮助,iisreset也没有帮助。 当我重新启动CacheCluster。。。。它工作并显示6

    因此,看起来该项在缓存中。windows服务将对其进行更新,但不会使该项目无效,该项目已过时,并由webapp使用

    或者。。。虽然是同一个对象,但webapp在缓存中有自己的条目,而win app有自己的条目(确实会失效)

    哪一个

    解决方案

    我自己解决了这个问题。EF包装器似乎使用查询字符串作为键来存储缓存中的项。因此,引用数据库中相同数据的两个不同查询(不管它们来自共享同一分布式缓存或相同应用程序的两个不同应用程序)将具有不同的键(不同的查询字符串),因此缓存中的位置不同。也许这不是黑白的,而是这样的

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

    这导致了我的问题,即我的windows服务执行更新,而webapp仍然从缓存中看到旧的更新,这只能通过执行Restart CacheCluster命令来解决

    那么我是如何解决这个问题的: 我的windows服务是由Quartz计划程序触发的批处理作业。完成后 我清除整个缓存:

    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不是线程安全的——我不知道这是否会导致问题,但每次阅读新内容都会解决这个问题


    如果您在读取时遇到此问题,那么您必须跟踪各种缓存的位置、哪些缓存没有得到更新以及原因。我猜在每个使用点都有不同的缓存配置选项。祝你好运……)

    谢谢你的评论。关于更新,我添加了一个更新我如何修复它。此外,在更新新实例之前读取新实例与乐观并发的整体思想背道而驰。那么row version列就没有额外的好处了。您只想在没有其他人同时更新对象时更新它。。。关于rea