私のWPFアプリケーションでSQLDependencyを使用しようとしています。DependencySQL、更新時の奇妙な動作
私はデータベース
行動1(データベースからの更新)を更新する方法に応じて、2つの異なる振る舞いを持っている:最初のデータベースの変更(顧客の更新)で
- を - >ソフトで受信クライアントリストが更新されます。
- 2回目のデータベース変更(顧客の更新)では、ソフトは通知を受け取りますが、クエリ結果は更新されません。データベースの変更は、「顧客の作成」されている場合は
- 私がne新規顧客
との通知を受け取る動作2(ソフトからの変更点):
私は顧客から、一人の顧客を選択それをリストアップして更新します。通知を受け取り、顧客リストが更新されます。私は保存行を再更新する場合、私はまだ通知を受け取りますが、クエリの結果は更新されません。
しかし!私が別の顧客を更新して変更すると、私はそれを複数回行うことができます。最初の一つだけのバグ(データベースから、最初の1の後、私は通知を受け取りますが、クエリの結果をとにかく更新されません)
コード:
#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 CustomerChangeMessages;
CREATE SERVICE CustomerChangeNotifications ON QUEUE CustomerChangeMessages ([http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]);
このリンクは有効ではありません –