I have finally, with an aggregate of an amazingly high numbers of dysfunctional examples, managed to receive change messages on a SqlDependency
object. This knowledge may prepare you for the inelegance or plain incorrectness of my code.
I have an ActiveList<T>: ObservableCollection<T>
class that listens for changes to it's DB table data source and repopulates itself. I use the following code to create and initialize the list and the SqlDependency
:
Constructor:
public ActiveList()
{
PopulateList();
}
Populate:
private void PopulateList()
{ Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, ((Action)(() =>
{
Clear();
using (var dbContext = new XTimeDbContext())
{
var set = dbContext.Set<T>().ToList();
this.AddRange(set);
}
})));
SubscribeNotifications();
}
Subscribe:
private void SubscribeNotifications()
{
const string dependencyQuery = "select TITLE_ACTIVE, TITLE_NAME from dbo.TITLE";
var dependency = new SqlDependency();
dependency.OnChange += DependencyOnChange;
var connectionString = _dbContext.Database.Connection.ConnectionString;
//SqlDependency.Stop(connectionString);
SqlDependency.Start(connectionString);
using (var sqn = new SqlConnection(connectionString))
{
sqn.Open();
using (var cmd = new SqlCommand(dependencyQuery, sqn))
{
cmd.Notification = null;
dependency.AddCommandDependency(cmd);
//dependency.OnChange += DependencyOnChange;
using (var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
while (reader.Read())
{
}
}
}
}
}
And, finally, listen:
private void DependencyOnChange(object sender, SqlNotificationEventArgs sqlNotificationEventArgs)
{
_trace.TraceInformation("DependencyOnChange called. Reason: '{0}', Source: '{1}', Type: '{2}'.", sqlNotificationEventArgs.Info, sqlNotificationEventArgs.Source,
sqlNotificationEventArgs.Type);
//if (!_isPopulating)
//{
// PopulateList();
//}
SqlDependency.Stop(_dbContext.Database.Connection.ConnectionString;);
SubscribeNotifications();
_trace.TraceInformation("DependencyOnChange completed.");
}
The code is in slight disarray due to huge amounts of small, experimental changes, but my main problem is that when I run the test app that uses an ActiveList
, I get the first change notification; my log shows "DependencyOnChange called". Then, the call to SqlDependency.Stop
, wherever I place it, generates an InvalidOperationException
, with the Message:
There is already an open DataReader associated with this Command which must be closed first.
I can find no 'dangling' DataReaders anywhere in my code, so what could be causing this?