[英]Unable to SaveChanges on a db update. Weird lazy loading behavior possibly?
[英]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:我没有做以下事情吗?
创建队列CustomerChangeMessages;
在队列上创建服务CustomerChangeNotifications CustomerChangeMessages([ http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification] );
我觉得这里发生了2种事情之一:1.您过早地删除了事件处理程序,或者2触发了主触发器使从属触发器失效了,因此它永远不会触发。
另外,您的问题可能与SqlDependency的所有启动和停止有关,无论何时我看到使用了SqlDependency,并且当我自己使用它时,它都是在应用程序启动时启动的,并在应用程序结束时停止的,事件处理程序和触发器本身的重新构造已用于操纵触发。
我会尝试几件事,(我在这里有点猜测,因为不清楚您要解决的问题是什么)1.将开始和停止移至应用程序开始和应用程序结束2.您还可以重新创建所有触发任何触发,因为它们相互依赖。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.