Использование SqlDependency приводит к постоянным обновлениям - PullRequest
7 голосов
/ 12 мая 2011

Я взял пример с этой страницы MSDN и использовал его в значительной степени дословно. При запуске код компилируется должным образом, но changeCount бесконечно увеличивается независимо от того, было ли изменение в возвращенных данных. Когда на самом деле изменение имеет , то dataGridView1 отражает изменение правильно. Почему мой SqlDependency кажется, что он запускается в цикле, хотя изменений, по-видимому, не было?

Вот источник:

#region Using directives
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;
using System.Windows.Forms;
#endregion

namespace PreAllocation_Check
{
    public partial class Form1 : Form
    {
        int           changeCount = 0;
        const string  tableName = "MoxyPosition";
        const string  statusMessage = "Last: {0} - {1} changes.";
        DataSet       dataToWatch = null;
        SqlConnection MoxyConn = null;
        SqlCommand    SQLComm = null;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            if (CanRequestNotifications())
            {
                SqlDependency.Start(GetConnectionString());

                if (MoxyConn == null)
                    MoxyConn = new SqlConnection(GetConnectionString());

                if (SQLComm == null)
                {
                    SQLComm = new SqlCommand(GetSQL(), MoxyConn);

                    SqlParameter prm = new SqlParameter("@Quantity", SqlDbType.Int);
                    prm.Direction = ParameterDirection.Input;
                    prm.DbType = DbType.Int32;
                    prm.Value = 100;
                    SQLComm.Parameters.Add(prm);
                }

                if (dataToWatch == null)
                    dataToWatch = new DataSet();

                GetData();
            }
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            SqlDependency.Stop(GetConnectionString());
            if (MoxyConn != null)
                MoxyConn.Close();
        }

        private bool CanRequestNotifications()
        {
            try
            {
                SqlClientPermission SQLPerm = new SqlClientPermission(PermissionState.Unrestricted);
                SQLPerm.Demand();
                return true;
            }
            catch
            {
                return false;
            }
        }

        private string GetConnectionString()
        {
            return "server=***;database=***;user id=***;password=***";
        }

        private void GetData()
        {
            dataToWatch.Clear();
            SQLComm.Notification = null;
            SqlDependency SQLDep = new SqlDependency(SQLComm);
            SQLDep.OnChange += new OnChangeEventHandler(SQLDep_OnChange);

            using (SqlDataAdapter adapter = new SqlDataAdapter(SQLComm))
            {
                adapter.Fill(dataToWatch, tableName);
                dataGridView1.DataSource = dataToWatch;
                dataGridView1.DataMember = tableName;
            }
        }

        private string GetSQL()
        {
            return "SELECT PortID, CONVERT(money, SUM(PreAllocPos), 1) AS PreAllocation, CONVERT(money, SUM(AllocPos), 1) AS Allocation, CONVERT(money, SUM(PreAllocPos) - SUM(AllocPos), 1) AS PreLessAlloc " +
                   "FROM MoxyPosition " +
                   "WHERE CONVERT(money, PreAllocPos, 1) <> CONVERT(money, AllocPos, 1) " +
                   "GROUP BY PortID " +
                   "ORDER BY PortID ASC;";
        }

        void SQLDep_OnChange(object sender, SqlNotificationEventArgs e)
        {
            ISynchronizeInvoke i = (ISynchronizeInvoke)this;

            if (i.InvokeRequired)
            {
                OnChangeEventHandler tempDelegate = new OnChangeEventHandler(SQLDep_OnChange);
                object[] args = { sender, e };
                i.BeginInvoke(tempDelegate, args);
                return;
            }

            SqlDependency SQLDep = (SqlDependency)sender;
            SQLDep.OnChange -= SQLDep_OnChange;

            changeCount++;
            DateTime LastRefresh = System.DateTime.Now;
            label1.Text = String.Format(statusMessage, LastRefresh.TimeOfDay, changeCount);

            GetData();
        }
    }
}

Редактировать: Стоит отметить, что в базе данных, с которой я хочу работать, в настоящее время не включена служба брокера, и поэтому для проверки своего кода я создал резервную копию своей целевой базы данных и восстановил ее с новым имя, затем побежал ALTER DATABASE my_db_name SET ENABLE_BROKER против него. Все мои тесты были в этой альтернативной базе данных, а это значит, что я единственный пользователь в ней.

Ответы [ 4 ]

4 голосов
/ 18 августа 2012

Это старый вопрос, но проблема в том, что ваш запрос не соответствует требованиям.

Краткий ответ:
добавить имя схемы в таблицу "FROM DBO.MoxyPosition " +

Более длинный ответ:
Вы можете увидеть список требований здесь , которые очень похожи на те, что создаются при создании индексированного представления. Когда SQL-зависимость зарегистрирована, если она недействительна, немедленно запускается уведомление, сообщающее, что оно недействительно. Когда вы думаете об этом, это имеет смысл, потому что как Visual Studio может знать, каковы внутренние требования для SQL Engine?

Итак, в вашей функции SQLDep_OnChange вы захотите взглянуть на причину зависимости, сработавшей. Причина в переменной e (информация, источник и тип). Подробности об объекте события можно найти здесь:

Для вашего конкретного случая обратите внимание, как MS описывает свойство Type:

Gets a value that indicates whether this notification is generated 
because of an actual change, OR BY THE SUBSCRIPTION.
1 голос
/ 27 июля 2011

У меня была похожая проблема. Оказывается, выполнение SELECT * FROM dbo.MyTable постоянно вызывало обновление. Изменение на SELECT Id, Column1, Column2 FROM dbo.MyTable решило проблему.

Не похоже, что вы используете * в своем запросе, но вы можете попытаться упростить свой запрос, чтобы увидеть, есть ли у вас проблема.

0 голосов
/ 17 октября 2013

посмотрите, что тип SqlNotificationEventArgs находится в вашем обработчике (определяется как показано ниже). Если вы видите, что он попадал сотни раз, и каждый раз Тип был подписан, тогда ваш SQL-запрос неверен - см. Рекомендации в других публикациях

private void HandleOnChange(object sender, SqlNotificationEventArgs e)
{
...

var someType = e.Type; /*If it is Subscribe, not Change, then you may have your SQL statement wrong*/
...
}
0 голосов
/ 12 июля 2011

У меня нет ответа на этот вопрос, но вы нарушили хотя бы одно из правил: http://msdn.microsoft.com/en-us/library/aewzkxxh.aspx

, когда вы не смогли использовать имена таблиц, состоящие из двух частей.Измените MoxyPosition на dbo.MoxyPosition и просмотрите правила, связанные выше.Я надеюсь, что это помогает, но что-то говорит мне, что здесь виноват другой человек.

...