Почему мой SqlCacheDependency HasChanged возвращается false, но почти сразу после изменения в true? - PullRequest
12 голосов
/ 22 декабря 2011

Я не могу понять, почему значение HasChanged моего объекта SqlCacheDependency первоначально возвращается из выполнения команды как ложное, но где-то почти сразу после того, как оно возвращается из базы данных, значение изменяется на true.

Иногда это происходит до того, как элемент даже вставляется в кеш, в результате чего кеш немедленно его отбрасывает, иногда после вставки, и я могу получить перечислитель, который видит ключ в кеше, но до того, как я дажеЦикл этого элемента в кэше был удален.

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 ]

6 голосов
/ 27 декабря 2011

Это было действительно, очень просто, так просто, что я полностью упустил это из виду.

В этой статье Создание запроса на уведомление , который я DID обыскиваю несколько раз, четко гласит:

Настройки параметров SET

Когда инструкция SELECT выполняется по запросу уведомления, соединение, которое отправляет запрос, должно иметь параметры для соединение устанавливается следующим образом:

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

Что ж, я прочитал, перечитал и перечитал спрок, и я до сих пор не увидел, что ANSI_NULLS и QUOTED_IDENTIFIER были "ВЫКЛ", а не ВКЛ.

Мой набор данных теперь кэширует и правильно сохраняет данные без ложных индикаторов изменений.

1 голос
/ 27 декабря 2011

У меня есть догадка, что проблема в вашем _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;

Важно

При создании кода выше я думаю, что обнаружил источник вашей проблемы: при установке сохраненного параметра proc вы используете _eR_ID, но когда вы устанавливаете paramHash, вы используете _eR_ID.Value.

Переписывание кода решит эту проблему, но я подозреваю, что это корень проблемы.

0 голосов
/ 11 декабря 2014

Столкнувшись с той же проблемой и найдя те же ответы в Интернете без какой-либо помощи, я повторно изучил недействительный ответ на подписку XML от профилировщика.

Я нашел пример на сайте поддержки msdn с немного другим порядком кода. Когда я попробовал это, я понял проблему - не открывайте свой объект соединения, пока вы не создадите объект команды и объект зависимости кэша. Вот порядок, которому вы должны следовать, и все будет хорошо:

  1. Обязательно включите уведомления (SqlCahceDependencyAdmin) и запустите SqlDependency.Start first
  2. Создать объект подключения
  3. Создайте объект команды и назначьте текст команды, тип и объект соединения (любая комбинация конструкторов, свойства настроек или использование CreateCommand).
  4. Создание объекта зависимости sql-кэша
  5. Открыть объект подключения
  6. Выполнить запрос
  7. Добавление элемента в кэш с использованием зависимости.

Если вы следуете этому порядку и соблюдаете все другие требования в своем утверждении select, у вас нет проблем с разрешениями, это будет работать!

Я полагаю, что проблема связана с тем, как .NET Framework управляет соединением, в частности с настройками. Я попытался переопределить это в моем тесте команды 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 частей для таблиц. В его случае предложение решило проблему. Посмотрев его код, я заметил, что основным отличием было ожидание открытия объекта подключения, пока ПОСЛЕ команды объекта И объект зависимости не были созданы. Мое единственное предположение скрыто (я еще не запустил рефлектор для проверки, поэтому только предположение), объект Соединения открывается по-другому, или порядок событий и команды происходят по-разному из-за этой ассоциации.

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

Надеюсь, это поможет кому-то еще в аналогичной проблеме.

...