【问题标题】:DependencySQL, weird behavior on updateDependencySQL,更新时的奇怪行为
【发布时间】:2016-08-09 09:43:30
【问题描述】:

我正在尝试在我的 WPF 应用程序中使用 SQLDependency。

我有 2 种不同的行为,具体取决于我更新数据库的方式

行为1(从数据库更新):

  • 第一次数据库更改(客户更新)-> 软件收到通知,更新客户列表。
  • 在第二次数据库更改(客户更新)时,软件收到通知,但查询结果未更新。
  • 如果数据库更改是“创建客户”,我会收到新客户通知

行为2(软变):

  • 我从客户列表中选择一位客户并对其进行更新。我收到通知,客户列表已更新。如果我重新更新保存行,我仍然会收到通知,但查询结果没有更新。

  • 但是!如果我更新另一个客户并更改它,我可以多次执行,查询结果还可以!只有第一个错误。(来自数据库,在第一个之后,我收到通知,但查询结果无论如何都没有更新)

    代码:

        #region Updater
    private IQueryable iCustomerquery = null;
    private ImmediateNotificationRegister<Customer> notification = null;
    RDatabase ctx = new RDatabase();
    
    void createCustomerRefreshQuery()
    {
    
        // Create the query.
        iCustomerquery = from p in ctx.Customers select p;
    
        notification = new ImmediateNotificationRegister<Customer>(ctx, iCustomerquery);
        notification.OnChanged += NotificationOnChanged;
    
    }
    
    
    /// <summary>
    /// When changed the data, the method will be invoked.
    /// </summary>
    void NotificationOnChanged(object sender, EventArgs e)
    {
        System.Windows.Application app = System.Windows.Application.Current;
        app.Dispatcher.BeginInvoke(new Action(UpdateCustomer), null);
    
    
    }
    
    void UpdateCustomer()
    {
    
    
        if (CanRequestNotifications())
        {
            Console.WriteLine("UPDATE");
    
            try
            {
                var customers = (iCustomerquery as DbQuery<Customer>).ToList();
                ClientList.Clear();
                foreach (var customer in customers)
                {
    
                    ClientList.Add(customer);
                    OnPropertyChanged("ClientList");
    
                }
            }
            catch (Exception ex)
            {
                if (ex.InnerException != null)
                {
                    Console.WriteLine(ex.Message + "(" + ex.InnerException.Message + ")");
                }
                else
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
        //iCustomerquery = from p in ctx.Customers select p;
    
    }
    
    
    private bool CanRequestNotifications()
    {
        // In order to use the callback feature of the
        // SqlDependency, the application must have
        // the SqlClientPermission permission.
        try
        {
            SqlClientPermission perm =
                new SqlClientPermission(
                PermissionState.Unrestricted);
    
            perm.Demand();
    
            return true;
        }
        catch (SecurityException se)
        {
            Console.WriteLine(se.Message, "Permission Error");
            return false;
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message, "Error");
            return false;
        }
    }
    /// <summary>
    /// Stop SqlDependency.
    /// </summary>
    private void StopSqlDependency(object sender, EventArgs e)
    {
        try
        {
            Console.WriteLine("Stop sql dependency");
            if (notification != null)
            {
                notification.Dispose();
                notification = null;
            }
        }
        catch (ArgumentException ex)
        {
            //MessageBox.Show(ex.Message, "Paramter Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        catch (Exception ex)
        {
            if (ex.InnerException != null)
            {
                Console.WriteLine(ex.Message + "(" + ex.InnerException.Message + ")", "Failed to Stop SqlDependency");
            }
            else
            {
                Console.WriteLine(ex.Message, "Failed to Stop SqlDependency");
            }
        }
    }
    
    
    #endregion
    

我使用了这个来自 msdn 的示例

立即通知注册:

    public class ImmediateNotificationRegister<TEntity> : IDisposable
    where TEntity : class
{
    private SqlConnection connection = null;
    private SqlCommand command = null;
    private IQueryable iquery = null;
    private ObjectQuery oquery = null;

    // Summary:
    //     Occurs when a notification is received for any of the commands associated
    //     with this ImmediateNotificationRegister object.
    public event EventHandler OnChanged;
    private SqlDependency dependency = null;

    /// <summary>
    /// Initializes a new instance of ImmediateNotificationRegister class.
    /// </summary>
    /// <param name="query">an instance of ObjectQuery is used to get connection string and 
    /// command string to register SqlDependency nitification. </param>
    public ImmediateNotificationRegister(ObjectQuery query)
    {
        try
        {
            this.oquery = query;

            QueryExtension.GetSqlCommand(oquery, ref connection, ref command);

            BeginSqlDependency();
        }
        catch (ArgumentException ex)
        {
            throw new ArgumentException("Paramter cannot be null", "query", ex);
        }
        catch (Exception ex)
        {
            throw new Exception(
                "Fails to initialize a new instance of ImmediateNotificationRegister class.", ex);
        }
    }

    /// <summary>
    /// Initializes a new instance of ImmediateNotificationRegister class.
    /// </summary>
    /// <param name="context">an instance of DbContext is used to get an ObjectQuery object</param>
    /// <param name="query">an instance of IQueryable is used to get ObjectQuery object, and then get  
    /// connection string and command string to register SqlDependency nitification. </param>
    public ImmediateNotificationRegister(DbContext context, IQueryable query)
    {
        try
        {
            this.iquery = query;

            // Get the ObjectQuery directly or convert the DbQuery to ObjectQuery.
            oquery = QueryExtension.GetObjectQuery<TEntity>(context, iquery);

            QueryExtension.GetSqlCommand(oquery, ref connection, ref command);

            BeginSqlDependency();
        }
        catch (ArgumentException ex)
        {
            if (ex.ParamName == "context")
            {
                throw new ArgumentException("Paramter cannot be null", "context", ex);
            }
            else
            {
                throw new ArgumentException("Paramter cannot be null", "query", ex);
            }
        }
        catch (Exception ex)
        {
            throw new Exception(
                "Fails to initialize a new instance of ImmediateNotificationRegister class.", ex);
        }
    }

    private void BeginSqlDependency()
    {
        // Before start the SqlDependency, stop all the SqlDependency.
        SqlDependency.Stop(QueryExtension.GetConnectionString(oquery));
        SqlDependency.Start(QueryExtension.GetConnectionString(oquery));

        RegisterSqlDependency();
    }

    private void RegisterSqlDependency()
    {
        if (command == null || connection == null)
        {
            throw new ArgumentException("command and connection cannot be null");
        }

        // Make sure the command object does not already have
        // a notification object associated with it.
        command.Notification = null;

        // Create and bind the SqlDependency object to the command object.
        dependency = new SqlDependency(command);
        dependency.OnChange += new OnChangeEventHandler(DependencyOnChange);

        // After register SqlDependency, the SqlCommand must be executed, or we can't 
        // get the notification.
        RegisterSqlCommand();
    }

    private void DependencyOnChange(object sender, SqlNotificationEventArgs e)
    {
        // Move the original SqlDependency event handler.
        SqlDependency dependency = (SqlDependency)sender;
        dependency.OnChange -= DependencyOnChange;

        if (OnChanged != null)
        {
            OnChanged(this, null);
        }

        // We re-register the SqlDependency.
        RegisterSqlDependency();
    }

    private void RegisterSqlCommand()
    {
        if (connection != null && command != null)
        {
            connection.Open();
            command.ExecuteNonQuery();
            connection.Close();
        }
    }

    /// <summary>
    /// Releases all the resources by the ImmediateNotificationRegister.
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected void Dispose(Boolean disposed)
    {
        if (disposed)
        {
            if (StopSqlDependency())
            {
                if (command != null)
                {
                    command.Dispose();
                    command = null;
                }

                if (connection != null)
                {
                    connection.Dispose();
                    connection = null;
                }

                OnChanged = null;
                iquery = null;
                dependency.OnChange -= DependencyOnChange;
                dependency = null;
            }
        }
    }

    /// <summary>
    /// Stops the notification of SqlDependency.
    /// </summary>
    /// <returns>If be success, returns true;If fails, throw the exception</returns>
    public Boolean StopSqlDependency()
    {
        try
        {
            SqlDependency.Stop(QueryExtension.GetConnectionString(oquery));
            return true;
        }
        catch (ArgumentException ex)
        {
            throw new ArgumentException("Parameter cannot be null.", "query", ex);
        }
        catch (Exception ex)
        {
            throw new Exception("Fails to Stop the SqlDependency in the ImmediateNotificationRegister class.", ex);
        }
    }

    /// <summary>
    /// The SqlConnection is got from the Query.
    /// </summary>
    public SqlConnection Connection
    { get { return connection; } }

    /// <summary>
    /// The SqlCommand is got from the Query.
    /// </summary>
    public SqlCommand Command
    { get { return command; } }

    /// <summary>
    /// The ObjectQuery is got from the Query.
    /// </summary>
    public ObjectQuery Oquery
    { get { return oquery; } }
}

查询扩展:

    public static class QueryExtension
{
    /// <summary>
    /// Return the ObjectQuery directly or convert the DbQuery to ObjectQuery.
    /// </summary>
    public static ObjectQuery GetObjectQuery<TEntity>(DbContext context, IQueryable query)
        where TEntity : class
    {
        if (query is ObjectQuery)
            return query as ObjectQuery;

        if (context == null)
            throw new ArgumentException("Paramter cannot be null", "context");

        // Use the DbContext to create the ObjectContext
        ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;
        // Use the DbSet to create the ObjectSet and get the appropriate provider.
        IQueryable iqueryable = objectContext.CreateObjectSet<TEntity>() as IQueryable;
        IQueryProvider provider = iqueryable.Provider;

        // Use the provider and expression to create the ObjectQuery.
        return provider.CreateQuery(query.Expression) as ObjectQuery;
    }

    /// <summary>
    /// Use ObjectQuery to get SqlConnection and SqlCommand.
    /// </summary>
    public static void GetSqlCommand(ObjectQuery query, ref SqlConnection connection, ref SqlCommand command)
    {
        if (query == null)
            throw new System.ArgumentException("Paramter cannot be null", "query");

        if (connection == null)
        {
            connection = new SqlConnection(QueryExtension.GetConnectionString(query));
        }

        if (command == null)
        {
            command = new SqlCommand(QueryExtension.GetSqlString(query), connection);

            // Add all the paramters used in query.
            foreach (ObjectParameter parameter in query.Parameters)
            {
                command.Parameters.AddWithValue(parameter.Name, parameter.Value);
            }
        }
    }

    /// <summary>
    /// Use ObjectQuery to get the connection string.
    /// </summary>
    public static String GetConnectionString(ObjectQuery query)
    {
        if (query == null)
        {
            throw new ArgumentException("Paramter cannot be null", "query");
        }

        EntityConnection connection = query.Context.Connection as EntityConnection;
        return connection.StoreConnection.ConnectionString;
    }

    /// <summary>
    /// Use ObjectQuery to get the Sql string.
    /// </summary>
    public static String GetSqlString(ObjectQuery query)
    {
        if (query == null)
        {
            throw new ArgumentException("Paramter cannot be null", "query");
        }

        string s = query.ToTraceString();

        return s;
    }

}

更新 1: 我应该没有做以下事情吗?

创建队列客户更改消息;

创建服务 CustomerChangeNotifications ON QUEUE CustomerChangeMes​​sages ([http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]);

【问题讨论】:

  • 该链接不再有效

标签: c# .net sql-server entity-framework sqldependency


【解决方案1】:

我觉得这里发生了两件事之一,1. 您正在过早地删除事件处理程序,或者 2 触发主触发器会使依赖触发器无效,因此它永远不会触发。

您的问题也可能与 SqlDependency 的所有启动和停止有关,每当我看到使用 SqlDependency 时,当我自己使用它时,它在应用程序启动时启动并在应用程序结束时停止,并且触发器本身的事件处理程序和重新创建已用于操纵触发。

我会尝试几件事,(我在这里有点猜测,因为不清楚您要解决的问题是什么) 1.将你的开始和停止移动到应用开始和应用结束 2. 您还可以在触发任何触发器时重新创建所有触发器,因为它们相互依赖。

【讨论】:

    猜你喜欢
    • 2016-11-15
    • 1970-01-01
    • 1970-01-01
    • 2017-02-01
    • 2020-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多