使用 SqlDependency 会导致不断更新

发布于 2024-11-06 09:44:17 字数 4643 浏览 1 评论 0原文

我从 此 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。我的所有测试都是在这个备用数据库上进行的,这意味着我是它的唯一用户。

I pulled an example from this MSDN page and have used it pretty much verbatim. When run the code compiles properly but changeCount increments endlessly whether or not there has actually been a change to the data returned. When a change actually has occurred dataGridView1 reflects the change correctly. Why does my SqlDependency seem like it's firing in a loop even though there apparently have been no changes?

Here's the source:

#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();
        }
    }
}

Edit: It's worth noting that the database I want to run this against does not currently have the Broker Service enabled, and so to test my code I backed up my target database and restored it with a new name, then ran ALTER DATABASE my_db_name SET ENABLE_BROKER against it. All of my testing has been on this alternate database, which means I'm the only user on it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

林空鹿饮溪 2024-11-13 09:44:17

这是一个老问题,但问题是你的查询不符合要求。

简短回答:
将模式名称添加到表“FROM DBO.MoxyPosition”+

更长的答案:
您可以在此处查看要求列表,这与创建索引视图的要求非常相似。注册 SQL 依赖项后,如果它无效,则会立即触发通知,让您知道它无效。仔细想想,这是有道理的,因为 Visual Studio 如何知道 SQL 引擎的内部需求是什么?

因此,在您的 SQLDep_OnChange 函数中,您需要查看触发依赖项的原因。原因在于 e 变量(信息、源和类型)。有关事件对象的详细信息可以在此处找到:

对于您的具体情况,请注意MS 描述 Type 属性:

Gets a value that indicates whether this notification is generated 
because of an actual change, OR BY THE SUBSCRIPTION.

This is an old question, but the problem is that your query doesn't meet the requirements.

Short answer:
add schema name to the table "FROM DBO.MoxyPosition " +

Longer answer:
You can see a list of requirements here, which are very similar to those of creating an indexed view. When a SQL Dependency is registered, if it is invalid the notification immediately fires letting you know it's invalid. When you think about it, this makes sense, because how can Visual studio know what the internal requirements are for the SQL Engine?

So in your SQLDep_OnChange function you'll want to look at the reason the dependency fired. The reason is in the e variable (info, source, and type). Details on the event object can be found here:

For your specific case notice how MS describes the Type property :

Gets a value that indicates whether this notification is generated 
because of an actual change, OR BY THE SUBSCRIPTION.
痴情 2024-11-13 09:44:17

我有类似的问题。事实证明,执行 SELECT * FROM dbo.MyTable 导致更新不断触发。更改为 SELECT Id, Column1, Column2 FROM dbo.MyTable 解决了该问题。

您似乎没有在查询中使用 *,但您可以尝试简化查询以查看问题是否仍然存在。

I had a similar problem. Turns out doing SELECT * FROM dbo.MyTable was causing the update to fire constantly. Changing to SELECT Id, Column1, Column2 FROM dbo.MyTable fixed the problem.

It doesn't look like you're using * in your query, but you might try simplifying your query to see if you still have the issue.

梅倚清风 2024-11-13 09:44:17

我对此没有答案,但您确实违反了至少一条规则: http://msdn.microsoft.com/en-us/library/aewzkxxh.aspx

当您无法使用两部分表名称时。将 MoxyPosition 更改为 dbo.MoxyPosition 并查看上面链接的规则。我希望它有帮助,但有些东西告诉我这里还有其他问题。

I don't have an answer for this, but you did break at least one of the rules here: http://msdn.microsoft.com/en-us/library/aewzkxxh.aspx

when you failed to use two-part table names. Change MoxyPosition to dbo.MoxyPosition and review the rules linked above. I hope it helps, but something tells me something else is at fault here.

望笑 2024-11-13 09:44:17

查看处理程序中 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*/
...
}

see what the Type of the SqlNotificationEventArgs is in your handler (defined as below). If you see it hit hundreds of times and the Type is Subscribe each time then your SQL statement is wrong - see guidelines in other postings

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*/
...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文