SqlDependency в ASP.NET - PullRequest
       19

SqlDependency в ASP.NET

4 голосов
/ 12 октября 2009

Я создал следующую инкапсуляцию для объекта SQL Dependency:

public class DependencyTracker
    {
        private SqlDependency _SQLDependency = null;

        public string ConnectionString
        { get; private set; }

        public string CommandNotifier
        { get; private set; }

        public delegate void Refresh();
        public event Refresh OnRefresh;

        public DependencyTracker(string connectionString, string commandNotifier)
        {
            ConnectionString = connectionString;
            CommandNotifier = commandNotifier;
        }

        public void StartDependency()
        {
            SqlDependency.Start(ConnectionString);
        }

        public void StopDependency()
        {
            SqlDependency.Stop(ConnectionString);
        }

        public void TrackForChanges()
        {
            using (SqlConnection sqlConn = new SqlConnection(ConnectionString))
            {
                sqlConn.Open();
                using (SqlCommand sqlCommand = new SqlCommand(CommandNotifier, sqlConn))
                {
                    sqlCommand.CommandType = CommandType.StoredProcedure;
                    _SQLDependency = new SqlDependency(sqlCommand);
                    _SQLDependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
                    sqlCommand.ExecuteReader();
                }
            }
        }

        void dependency_OnChange(object sender, SqlNotificationEventArgs e)
        {
            SqlDependency sqlDependency = (SqlDependency)sender;
            sqlDependency.OnChange -= dependency_OnChange;

            if (OnRefresh != null)
            {
                OnRefresh();
            }
        }

        public bool HasChanges
        {
            get
            {
                return _SQLDependency.HasChanges;
            }
        }
    }

Это не оригинальная работа, она основана на этом тексте. Из своего репозитория я делаю следующее:

public bool Updatexxx(Ixxx xsxs)
        {
            try
            {
                SqlConnection sqlConn = new SqlConnection(_ConnectionString);
                sqlConn.Open();
                ...

                bool result = sqlComm.ExecuteNonQuery() == 1;

                _ResetEvent.WaitOne();

                return result;
            }
            catch ...
            catch ...
        }

Обратный вызов:

public void RefreshData()
        {
            FindAllxxx();
            _ResetEvent.Set();
        }

и

public HashSet<Iddd> Finddadas()
        {
            DependencyTracker tracker = _Container.Resolve<DependencyTracker>("dada");
            UnityHashSet<IT24Route> hash = _Container.Resolve<UnityHashSet<dadas>>("Tdsadas");
            if (tracker.HasChanges || hash.Count == 0)
            {
                hash = new UnityHashSet<dsda>(_Container);
                hash.ImportHashSet(FindAlldsdsNonCached());
                _Container.RegisterInstance<UnityHashSet<dsds>>("dasda", hash);
                tracker.TrackForChanges();
            }
            return hash;
        }

, а FindAllXXXNonCached () делает реальный выбор из базы данных.Как видите, я все кеширую.Мой вопрос: почему это НЕ работает?Симптом: из трекера зависимостей обратный вызов вызывается дважды, затем блокируется.Я реализовал это, потому что мои уведомления приходили после начала обновления страницы.Я попытался установить событие ручного сброса, чтобы дать уведомление, а затем установить событие ручного сброса и обновить пользовательский интерфейс.Как я уже сказал, после прохождения дважды через OnChange он зависает.Что я могу сделать?Трекер зависимостей является экземпляром, зарегистрированным в контейнере Unity, тип репозитория зарегистрирован.

1 Ответ

6 голосов
/ 12 октября 2009
void dependency_OnChange(object sender, SqlNotificationEventArgs e)

Ожидается, что вы осмотрите SqlNotificationEventArgs и увидите, о чем вас уведомили, об изменении данных или о чем-то еще. Отметьте Информация как Вставить / Обновить / Удалить . Проверьте Источник , чтобы быть Данные . Отметьте Тип как Изменить .

Скорее всего, ваш запрос незамедлительно проинформирован о том, что он не соответствует ограничениям , наложенным на уведомления о запросах . Да, я знаю, что ссылка указывает на индексированные представления, и если вы хотите понять, почему, прочитайте Таинственное уведомление .

У вас также есть состояние гонки между обновлением, ожидающим _ResetEvent, и сигнализацией обратного вызова _ResetEvent. T1 вызывает обновление. Тем временем происходит несвязанное обновление данных и вызывается обратный вызов. _ResetEvent установлен. T1 завершает обновление и ожидает _ResteEvent, о котором сообщается, поэтому оно продолжается. Вызывающая сторона предполагает, что произошел обратный вызов для собственного обновления, и кэш обновлен, но это не так.

Вторая более серьезная проблема заключается в том, что при наличии транзакций код неверен. UpdateXXX предполагает, что обратный вызов для его собственного обновления будет происходить немедленно и ждет его. Уведомление о запросе будет доставлено модулем только после фиксации обновления, поэтому при наличии TransactionScope метод UpdateXXX блокирует ожидание уведомления, которое не может быть получено до тех пор, пока UpdateXXX не вернется (живая взаимоблокировка).

Также не ясно, какова цель TrackForChanges. Вы читаете SqlDataReader (sqlCommand.ExecuteReader), но игнорируете результат. С помощью уведомлений о запросах вы отправляете запрос, читаете свой результат , и вы будете уведомлены об изменении этого результата.

И, наконец, никогда не считывает данные в обратном вызове уведомления SqlDependency .

...