![](/img/trans.png)
[英]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.