¿Por qué recibo una excepción de "lector de datos abiertos" con mi suscripción a SqlDependency?
Frecuentes
Visto 393 veces
0
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.
Tengo una 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();
}
Poblar:
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();
}
Suscribir:
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?
2 Respuestas
0
Deberías llamar SqlDependency.Start(connectionString);
just once at beginning and SqlDependency.Stop(_dbContext.Database.Connection.ConnectionString;);
just once at the end (when you decide you will not follow changes). These commands creates and drops queues for change events.
Next lines you should call whenever you need to subscribe for next change.
var dependency = new SqlDependency();
dependency.OnChange += DependencyOnChange;
por ejemplo:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
SqlDependency.Start("server=<MyServer>;database=<MyDB>;User ID=<user>;Password=<pwd>;Integrated Security=false;");
Console.WriteLine("Started..");
get_msg();
Console.ReadLine();
SqlDependency.Stop("server=<MyServer>;database=<MyDB>;User ID=<user>;Password=<pwd>;Integrated Security=false;");
}
private static void get_msg()
{
using (SqlConnection con =
new SqlConnection("server=<MyServer>;database=<MyDB>;User ID=<user>;Password=<pwd>;Integrated Security=false;"))
{
SqlCommand com = new SqlCommand("SELECT MyTableID, SomeText FROM dbo.MyTable ", con);
SqlDependency dependency = new SqlDependency(com);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
con.Open();
com.ExecuteNonQuery();
}
}
static void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
Console.WriteLine("dependency Info = {0}, time: {1}",e.Info, DateTime.Now);
get_msg();
}
}
}
Should keep in mind, that SQL Dependency is for situations when changes in DB are not frequent. In code example subscription for the next change is instant, but it would be good idea to wait for a while.
respondido 28 nov., 13:08
0
maybe just a workaround ... but have you tried to set MultipleActiveResultSets to true on your connection?
respondido 27 nov., 13:10
It is always true in all my connections. It hasn't ever caused me some of the trouble it can cause. - ProfK
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas .net ado.net sqldependency or haz tu propia pregunta.
Entonces yo llamaría
SqlDependency.Start
when I activate my parent model, which contains a collection of 'table' models that listen for changes, andSqlDependency.Stop
when I deactivate that, not for individual notifications? BTW, this is for changes to lookup lists etc. so they are infrequent. - ProfK@ProfK yes. Notification fires just once, after that you should subscribe for next change. - Janis