簡體   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