繁体   English   中英

DependencySQL,更新时的行为异常

[英]DependencySQL, weird behavior on update

我正在尝试在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:我没有做以下事情吗?

创建队列CustomerChangeMes​​sages;

在队列上创建服务CustomerChangeNotifications CustomerChangeMes​​sages([ http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification] );

我觉得这里发生了2种事情之一:1.您过早地删除了事件处理程序,或者2触发了主触发器使从属触发器失效了,因此它永远不会触发。

另外,您的问题可能与SqlDependency的所有启动和停止有关,无论何时我看到使用了SqlDependency,并且当我自己使用它时,它都是在应用程序启动时启动的,并在应用程序结束时停止的,事件处理程序和触发器本身的重新构造已用于操纵触发。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM