DependencySQL,更新时奇怪的行为

时间:2016-08-09 09:43:30

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

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

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

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

  • 首次更改数据库(客户更新) - >软接收通知,客户列表更新。
  • 在第二次数据库更改(客户更新)时,软件会收到通知,但查询结果未更新。
  • 如果数据库更改为"创建客户"我收到了新客户的通知

行为2(从软更改):

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

  • 但是!如果我更新另一个客户并更改它,我可以多次执行,查询结果还可以!只有第一个bug。(从数据库开始,在第一个之后,我收到通知,但查询结果还没有更新)

    代码:

        #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

中使用了这个样本

ImmediateNotificationRegister:

    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: 我不应该做以下事情吗?

CREATE QUEUE CustomerChangeMes​​sages;

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

1 个答案:

答案 0 :(得分:0)

我觉得这里发生了两件事之一,1。你过早地删除事件处理程序,或者2触发主触发器使依赖触发器无效,从而不会触发它。

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

我会尝试一些事情,(我有点猜测,因为目前还不清楚你要解决的问题是什么) 1.将您的开始和停止移动到应用程序开始和应用程序结束 2.每当触发任何触发器时,您也可以重新创建所有触发器,因为它们彼此依赖。