为什么我的SqlCacheDependency HasChanged返回false但几乎在更改为true后立即返回?

时间:2011-12-22 16:43:52

标签: c# sql-server-2008 asp.net-3.5 sqlcachedependency

我无法弄清楚为什么我的HasChanged对象的SqlCacheDependency值最初从命令执行中返回为false,但是在它从数据库返回后几乎立即返回,值变为真正。

有时这会在项目被插入缓存之前发生,导致缓存立即丢弃它,有时它是在插入之后,我可以抓住一个枚举器,它在缓存中看到密钥但在我甚至循环到那之前缓存中的项目已被删除。

SPROC:

ALTER PROCEDURE [dbo].[ntz_dal_ER_X_Note_SelectAllWER_ID]
        @ER_ID int
AS
BEGIN
    SELECT
        ER_X_Note_ID,
        ER_ID,
        Note_ID
    FROM dbo.ER_X_Note e
    WHERE
        ER_ID = @ER_ID
END

数据库是MS SQL Server 2008,已启用代理服务,并且某些输出会缓存并保持缓存状态。例如,这个工作正常:

ALTER PROC [dbo].[ntz_dal_GetCacheControllerByEntityName] (
    @Name varchar(50)
) AS
BEGIN
    SELECT 
        CacheController_ID,
        EntityName,
        CacheEnabled,
        Expiration
    From dbo.CacheController cc
    WHERE   EntityName = @Name
END

调用SPROC失败的代码:

    DataSet toReturn;
    Hashtable paramHash = new Hashtable();
    paramHash.Add("ER_ID", _eR_ID.IsNull ? null : _eR_ID.Value.ToString());
    string cacheName = BuildCacheString("ntz_dal_ER_X_Note_SelectAllWER_ID", paramHash);
    toReturn = (DataSet)GetFromCache(cacheName);
    if (toReturn == null)
    {

        // Set up parameters (1 input and 0 output)
        SqlParameter[] arParms = {
                new SqlParameter("@ER_ID", _eR_ID),
            };
        SqlCacheDependency scd;

        // Execute query.
        toReturn = _dbTransaction != null 
            ? _dbConnection.ExecuteDataset(_dbTransaction, "dbo.[ntz_dal_ER_X_Note_SelectAllWER_ID]", out scd, arParms) 
            : _dbConnection.ExecuteDataset("dbo.[ntz_dal_ER_X_Note_SelectAllWER_ID]", out scd, arParms);

        AddToCache(cacheName, toReturn, scd);
    }

    return toReturn;

有效的代码

        const string sprocName = "ntz_dal_GetCacheControllerByEntityName";
        string cacheControlPrefix = "CacheController_" + CachePrefix;
        CacheControl controller = (CacheControl)_cache[cacheControlPrefix];
        if (controller == null)
        {
            try
            {
                SqlParameter[] arParms = {
                                             new SqlParameter("@Name", CachePrefix),
                                         };
                SqlCacheDependency sqlCacheDependency;

                // Execute query.
                DataSet result = _dbTransaction != null
                                     ? _dbConnection.ExecuteDataset(_dbTransaction, sprocName, out sqlCacheDependency, arParms)
                                     : _dbConnection.ExecuteDataset(sprocName, out sqlCacheDependency, arParms);

                controller = result.Tables[0].Rows.Count == 0
                                 ? new CacheControl(false)
                                 : new CacheControl(result.Tables[0].Rows[0]);

                _cache.Insert(cacheControlPrefix, controller, sqlCacheDependency);
            }
            catch (Exception ex)
            {
                // if sproc retreival fails cache the result of false so we don't keep trying
                // this is the only case where it can be added with no expiration date
                controller = new CacheControl(false);

                // direct cache insert, no dependency, no expiration, never try again for this entity
                if (HttpContext.Current != null && UseCaching && _cache != null) _cache.Insert(cacheControlPrefix, controller);
            }
        }
        return controller;

AddToCache方法已超载,并且其中包含更多测试;工作方法中的直接_cache.Insert是绕过那些其他测试。工作代码有助于确定是否应该发生数据库缓存。

您可以看到,当最初检索到“非工作”数据时,一切正常:

enter image description here

但是在某个地方随机出现,在这个例子中,只是进入下一个方法

enter image description here

但数据根本没有变化;我是唯一一个接触这个数据库实例的人。

3 个答案:

答案 0 :(得分:6)

真的,非常简单,如此简单,我完全忽略了它。

在本文Creating a Query for Notification中,我 DID 多次搜索,它明确指出:

  

设置选项设置

     

当在通知请求下执行SELECT语句时,   提交请求的连接必须具有该选项   连接设置如下:

ANSI_NULLS ON
ANSI_PADDING ON
ANSI_WARNINGS ON
CONCAT_NULL_YIELDS_NULL ON
QUOTED_IDENTIFIER ON
NUMERIC_ROUNDABORT OFF
ARITHABORT ON

好吧,我读了并重新阅读并重新读取了sproc,我仍然没有看到ANSI_NULLS和QUOTED_IDENTIFIER都是“OFF”,而不是ON。

我的数据集现在正在缓存并保留数据,没有错误的变化指标。

答案 1 :(得分:1)

我预感到问题出在您的_eR_ID上。我认为您应该尝试将局部变量添加到使用_eR_ID的不可能值的失败过程,例如-1。我从不相信当涉及空值时会发生什么,我认为这可能是你问题的根源。

以下是我推荐的修改版本:

DataSet toReturn;
Hashtable paramHash = new Hashtable();

int local_er_ID = eR_ID.IsNull ? -1 : _eR_ID.Value;
paramHash.Add("ER_ID", local_eR_ID.ToString());

string cacheName = BuildCacheString("ntz_dal_ER_X_Note_SelectAllWER_ID", paramHash);
toReturn = (DataSet)GetFromCache(cacheName);
if (toReturn == null)
{

    // Set up parameters (1 input and 0 output)
    SqlParameter[] arParms = {
            new SqlParameter("@ER_ID", local_eR_ID),
        };
    SqlCacheDependency scd;

    // Execute query.
    toReturn = _dbTransaction != null 
        ? _dbConnection.ExecuteDataset(_dbTransaction, "dbo.[ntz_dal_ER_X_Note_SelectAllWER_ID]", out scd, arParms) 
        : _dbConnection.ExecuteDataset("dbo.[ntz_dal_ER_X_Note_SelectAllWER_ID]", out scd, arParms);

    AddToCache(cacheName, toReturn, scd);
}

return toReturn;

重要

在创建上述代码时,我认为我发现了问题的根源:设置存储过程参数时,您使用的是_eR_ID,但是当您设置paramHash时,您正在使用_eR_ID.Value

代码重写将解决这个问题,但我怀疑这是问题的根源。

答案 2 :(得分:0)

遇到同样的问题并在没有任何帮助的情况下在线查找相同的答案,我正在重新分析来自探查器的xml无效订阅响应。

我在msdn支持网站上找到了一个代码顺序略有不同的示例。当我尝试它时,我意识到了问题 - 在创建命令对象和缓存依赖项对象之前,请不要打开连接对象。这是您必须遵循的顺序,一切都会很好:

  1. 确保启用通知(SqlCahceDependencyAdmin)并首先运行SqlDependency.Start
  2. 创建连接对象
  3. 创建命令对象并分配命令文本,类型和连接对象(构造函数的任意组合,设置属性或使用CreateCommand)。
  4. 创建sql缓存依赖项对象
  5. 打开连接对象
  6. 执行查询
  7. 使用依赖项将项目添加到缓存。
  8. 如果您按照此顺序操作并遵循select语句的所有其他要求,请不要有任何权限问题,这将有效!

    我认为这个问题与.NET框架如何管理连接有关,特别是设置了哪些设置。我尝试在我的sql命令测试中覆盖它,但它从未起作用。这只是猜测 - 我所知道的是改变订单立即解决问题。

    我能够将以下内容拼凑到msdn帖子中。

    这篇文章是无效订阅的更常见原因之一,并显示了.Net客户端如何设置与通知所需的属性形成对比的属性。

    https://social.msdn.microsoft.com/Forums/en-US/cf3853f3-0ea1-41b9-987e-9922e5766066/changing-default-set-options-forced-by-net?forum=adodotnetdataproviders

    然后这篇帖子来自一个用户,他和我一样,将代码缩减为最简单的格式。我的原始代码模式与他的相似。

    https://social.technet.microsoft.com/Forums/windows/en-US/5a29d49b-8c2c-4fe8-b8de-d632a3f60f68/subscriptions-always-invalid-usual-suspects-checked-no-joy?forum=sqlservicebroker

    然后我发现这个帖子,也是一个非常简单的问题减少,只有他是一个简单的问题 - 表格需要2个部分名称。在他的案例中,该建议解决了这个问题。在查看他的代码后,我注意到主要区别在于等待打开连接对象,直到命令对象和依赖关系对象创建之后。我唯一的假设是在幕后(我还没有启动反射器来检查所以只是一个假设)Connection对象以不同的方式打开,或者事件和命令的顺序以不同的方式发生,因为这种关联。

    https://social.msdn.microsoft.com/Forums/sqlserver/en-US/bc9ca094-a989-4403-82c6-7f608ed462ce/sql-server-not-creating-subscription-for-simple-select-query-when-using-sqlcachedependency?forum=sqlservicebroker

    我希望这可以帮助其他人处理类似问题。