Подтвердить что ты не робот

Почему мой SqlCacheDependency HasChanged возвращается с ошибкой, но почти сразу после изменений в true?

Я не могу понять, почему значение HasChanged моего объекта SqlCacheDependency возвращается изначально из выполнения команды как false, но где-то почти сразу после его возврата из базы данных значение меняется на 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 в методе работы состоит в том, чтобы обойти эти другие тесты. Рабочий код помогает определить, должно ли происходить кэширование db вообще.

Вы можете видеть, что при первоначальном извлечении "неработающих" данных все в порядке:

enter image description here

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

enter image description here

И все же данные НЕ меняются; Я единственный, кто касается этого экземпляра базы данных.

4b9b3361

Ответ 1

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

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

Настройки опций SET

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

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

Хорошо, я прочитал и перечитал и RE-перечитал sproc, и я до сих пор не видел, что оба ANSI_NULLS и QUOTED_IDENTIFIER были "OFF", а не ON.

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

Ответ 2

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

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

Ответ 3

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

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

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

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

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

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