我试图在我的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 CustomerChangeMessages;
创建服务CustomerChangeNotifications ON QUEUE CustomerChangeMessages ([http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]);
答案 0 :(得分:0)
我觉得这里发生了两件事之一,1。你过早地删除事件处理程序,或者2触发主触发器使依赖触发器无效,从而不会触发它。
此外,您的问题可能与SqlDependency的所有启动和停止有关,只要我看到SqlDependency使用,当我自己使用它时,它已经在应用程序启动时启动并在应用程序结束时停止,触发器本身的事件处理程序和重新创建已被用于操纵触发。
我会尝试一些事情,(我有点猜测,因为目前还不清楚你要解决的问题是什么) 1.将您的开始和停止移动到应用程序开始和应用程序结束 2.每当触发任何触发器时,您也可以重新创建所有触发器,因为它们彼此依赖。