SqlDependency_OnChange() 没有开火

SqlDependency_OnChange() not firing

第一次使用SqlDependency,希望能找到问题的答案

我面临的问题是 SqlDependency_OnChange 事件没有正确触发

我在数据库中启用了代理

ALTER DATABASE databsename SET ENABLE_BROKER;

并将数据库所有者更改为 sa

ALTER AUTHORIZATION ON databsename TO sa;

这是我创建的 Table DDL

CREATE TABLE [dbo].[user_log](
    [user_log_id] [bigint] NOT NULL,
    [user_name] [nvarchar](100) NULL,
    [action_type_id] [int] NULL,
    [document_type_id] [int] NULL,
    [document_id] [nvarchar](20) NULL,
    [description] [nvarchar](200) NULL,
    [action_date] [datetime] NULL,
    [seen] [bit] NULL,
 CONSTRAINT [PK_user_log] PRIMARY KEY CLUSTERED 
(
    [user_log_id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

并在 visual studio

中编写了这段代码
    public User_Actions_Log_Form()
    {

        InitializeComponent();
        try
        {
            SqlClientPermission SCP = new SqlClientPermission(System.Security.Permissions.PermissionState.Unrestricted);
            SCP.Demand();
        }
        catch (Exception)
        {
            throw;
        }

        DA.DataAccess DAL = new DA.DataAccess();
        SqlDependency.Stop(DAL.MyConnectionString().ConnectionString);
        SqlDependency.Start(DAL.MyConnectionString().ConnectionString);

    }

    DataTable dt = new DataTable();
    public void SearchUserLog()
    {

        BL.UserLogBL usr_log_bl = new BL.UserLogBL();
        usr_log_bl.UserName = CBUser.SelectedValue == null ? null : CBUser.SelectedValue.ToString();
        usr_log_bl.ActionTypeID = CBActionType.SelectedValue == null ? null : CBActionType.SelectedValue.ToString();
        usr_log_bl.DocumentTypeID = CBDocumentType.SelectedValue == null ? null : CBDocumentType.SelectedValue.ToString();
        usr_log_bl.DateFrom = DTPFrom.Checked? DTPFrom.Value.Date:(DateTime?)null;
        usr_log_bl.DateTo = DTPTo.Checked ? DTPTo.Value.Date.AddSeconds(86340) : (DateTime?)null;
        DA.DataAccess DAL = new DA.DataAccess();

        using (SqlConnection con = new SqlConnection(DAL.MyConnectionString().ConnectionString))
        {
            SqlCommand cmd = new SqlCommand();
            if (con.State == ConnectionState.Closed)
            {
                con.Open();
            }
            cmd.Connection = con;
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandText = "dbo.ManageUserLog";
            SqlParameter[] para = new SqlParameter[7];
            para[0] = new SqlParameter("@check", "s");
            para[1] = new SqlParameter("@user_name", usr_log_bl.UserName);
            para[2] = new SqlParameter("@action_type_id", usr_log_bl.ActionTypeID);
            para[3] = new SqlParameter("@document_type_id", usr_log_bl.DocumentTypeID);
            para[4] = new SqlParameter("@date_from", usr_log_bl.DateFrom);
            para[5] = new SqlParameter("@date_to", usr_log_bl.DateTo);
            para[6] = new SqlParameter("@seen", usr_log_bl.Seen);
            cmd.Parameters.AddRange(para);
            var depenedency = new SqlDependency(cmd);

            cmd.Notification = null;
            depenedency.OnChange += new OnChangeEventHandler(sqlDependency_OnChange);
            dt.Rows.Clear();
            dt.Load(cmd.ExecuteReader(CommandBehavior.CloseConnection));

            dataGridView1.DataSource = dt;


        }
    }

    private void sqlDependency_OnChange(object sender, SqlNotificationEventArgs e)
    {

        SqlDependency SD = sender as SqlDependency;
        SD.OnChange -= sqlDependency_OnChange;
        if (OnNewUserActionsLogForm != null)
        {
            User_Actions_Log_Form_OnNewHome();
        }
    }

    public delegate void New_User_Actions_Log_Form();
    public event New_User_Actions_Log_Form OnNewUserActionsLogForm;
    private void User_Actions_Log_Form_Load(object sender, EventArgs e)
    {
        OnNewUserActionsLogForm += new New_User_Actions_Log_Form(User_Actions_Log_Form_OnNewHome);

        SearchUserLog();
    }

    private void User_Actions_Log_Form_OnNewHome()
    {
        ISynchronizeInvoke i = (ISynchronizeInvoke)this;
        if (i.InvokeRequired)
        {
            New_User_Actions_Log_Form dd = new New_User_Actions_Log_Form(User_Actions_Log_Form_OnNewHome);
            i.BeginInvoke(dd, null);
            return;
        }
        SearchUserLog();
    }

这是我调用的 sql 过程

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER proc [dbo].[ManageUserLog]

(
    @user_name nvarchar(100) = null,
    @action_type_id int = null,
    @document_type_id int = null,
    @date_from datetime = null,
    @date_to datetime = null,
    @seen bit = null
    )
as
begin
    select user_log_id,
                [user_name],
                dbo.GetActionTypeByID(action_type_id) as action_type,
                dbo.GetDocumentTypeByID(document_type_id) as document_type,
                document_id,
                [description],
                action_date,
                seen
        from dbo.user_log
        where (@user_name is null or [user_name] = @user_name)
          and (@action_type_id is null or action_type_id = @action_type_id)
          and (@document_type_id is null or document_type_id = @document_type_id)
          and (action_date between @date_from and @date_to)
          and (seen = @seen)
end

谁能帮我解决这个问题

下面是一个 Minimal, Complete, and Verifiable example 的工作 Sqldependency,它使用带有硬编码参数值的简化版本的 WinForm 应用程序。我修改了您问题中的存储过程以删除函数调用(对 SqlDependency 通知无效)并从 C# 代码中删除了 @check SqlCommand 参数,因为该参数未在存储过程中定义。

此示例适用于我的系统,因此我希望它会在数据更改时触发您的开发箱上的 OnChange 处理程序。

T-SQL设置码:

CREATE DATABASE YourDatabase;
GO
ALTER DATABASE YourDatabase SET ENABLE_BROKER;
GO
USE YourDatabase;
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[user_log](
    [user_log_id] [bigint] NOT NULL,
    [user_name] [nvarchar](100) NULL,
    [action_type_id] [int] NULL,
    [document_type_id] [int] NULL,
    [document_id] [nvarchar](20) NULL,
    [description] [nvarchar](200) NULL,
    [action_date] [datetime] NULL,
    [seen] [bit] NULL,
    CONSTRAINT [PK_user_log] PRIMARY KEY CLUSTERED 
    (
    [user_log_id] ASC
    )
);
GO
INSERT INTO dbo.user_log(user_log_id, user_name, action_date, seen)
    VALUES(1,'test', '2018-04-15T00:00:00', 1);
GO
CREATE proc [dbo].[ManageUserLog]
(
    @user_name nvarchar(100) = null,
    @action_type_id int = null,
    @document_type_id int = null,
    @date_from datetime = null,
    @date_to datetime = null,
    @seen bit = null
    )
as
begin
    select user_log_id,
                [user_name],
                --dbo.GetActionTypeByID(action_type_id) as action_type,
                --dbo.GetDocumentTypeByID(document_type_id) as document_type,
                document_id,
                [description],
                action_date,
                seen
        from dbo.user_log
        where (@user_name is null or [user_name] = @user_name)
          and (@action_type_id is null or action_type_id = @action_type_id)
          and (@document_type_id is null or document_type_id = @document_type_id)
          and (action_date between @date_from and @date_to)
          and (seen = @seen)
end
GO

GRANT EXEC ON dbo.ManageUserLog TO public;
GO

C# WinForm 代码:​​

using System;
using System.Data;
using System.Data.SqlClient;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {

        static string connectionString = @"Data Source=.;Initial Catalog=YourDatabase;Integrated Security=SSPI";

        public Form1()
        {
            InitializeComponent();
            SqlDependency.Start(connectionString);
            SearchUserLog();
        }

        public void SearchUserLog()
        {
            DataTable dt = new DataTable();
            using (SqlConnection con = new SqlConnection(connectionString))
            using (SqlCommand cmd = new SqlCommand())
            {
                con.Open();
                cmd.Connection = con;
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.CommandText = "dbo.ManageUserLog";
                SqlParameter[] para = new SqlParameter[6];
                para[0] = new SqlParameter("@user_name", "test");
                para[1] = new SqlParameter("@action_type_id", System.DBNull.Value);
                para[2] = new SqlParameter("@document_type_id", System.DBNull.Value);
                para[3] = new SqlParameter("@date_from", DateTime.Parse("2018-04-15"));
                para[4] = new SqlParameter("@date_to", DateTime.Parse("2018-04-16"));
                para[5] = new SqlParameter("@seen", 1);
                cmd.Parameters.AddRange(para);
                var depenedency = new SqlDependency(cmd);

                depenedency.OnChange += new OnChangeEventHandler(sqlDependency_OnChange);
                dt.Rows.Clear();
                dt.Load(cmd.ExecuteReader(CommandBehavior.CloseConnection));
            }
        }

        private void sqlDependency_OnChange(object sender, SqlNotificationEventArgs e)
        {
            MessageBox.Show($"OnChange Event fired. SqlNotificationEventArgs: Info={e.Info}, Source={e.Source}, Type={e.Type}\r\n");

            //resubscribe only if valid
            if ((e.Info != SqlNotificationInfo.Invalid)
                && (e.Type != SqlNotificationType.Subscribe))
            {
                SearchUserLog();
            }
        }
    }
}

T-SQL 触发 OnChange 处理程序的代码:

DECLARE @ID int = (SELECT MAX(user_log_id)+1 FROM dbo.user_log);
INSERT INTO dbo.user_log(user_log_id, user_name, action_date, seen)
    VALUES(@ID,'test', '2018-04-15T00:00:00', 1);
GO