SqlDependency with EntityFramework 6 (async)

user1032657 picture user1032657 · Jun 27, 2013 · Viewed 10.5k times · Source

I'm using the EF 6 async querying features, such as

var list = await cx.Clients.Where(c => c.FirstName.Length > 0).ToListAsync();

I want to also start SQL dependencies on these queries so that I can get notified when the data in the database changes. I can do this using the System.Runtime.Remoting.Messaging.CallContext as follows:

    async Task GetData()
    {
        using (ClientsContext context = new ClientsContext()) // subclass of DbContext
        {

            SqlDependency.Start(context.Database.Connection.ConnectionString);
            SqlDependency dependency = new SqlDependency();
            dependency.OnChange += (sender, e) =>
                {
                    Console.Write(e.ToString()); 
                };

            System.Runtime.Remoting.Messaging.CallContext.SetData("MS.SqlDependencyCookie", dependency.Id);
            var list = await context.Clients.Where(c => c.FirstName.Length > 0).ToListAsync();
        }
    }

.. and it works fine. But I am running into an issue if I want to have an SqlDependency on more than one query. If I have two async methods similar to GetData() above, and I run both at the same time, only the first one will get change notifications. I assume that this is due to the CallContext having the cookie set by each method in succession. If I wait for the first async method to complete, then call the second, they both get change notifications as expected. Is there any solution to this?

Answer

Kenneth Ito picture Kenneth Ito · Jun 4, 2014

I'm not too familiar with SqlDependency, but the below will allow your CallContext to have the correct value at the time ToListAsync is called (When multiple calls are running). Proof of concept here, https://dotnetfiddle.net/F8FnFe

    async Task<List<Client>> GetData()
    {
        using (ClientsContext context = new ClientsContext()) // subclass of DbContext
        {
            SqlDependency.Start(context.Database.Connection.ConnectionString);
            SqlDependency dependency = new SqlDependency();
            dependency.OnChange += (sender, e) =>
            {
                Console.Write(e.ToString());
            };

            Task<List<Client>> task = Task<Task<List<Client>>>.Factory.StartNew(async () =>
            {
                System.Runtime.Remoting.Messaging.CallContext.SetData("MS.SqlDependencyCookie", dependency.Id);
                var list = await context.Clients.Where(c => c.FirstName.Length > 0).ToListAsync();
            }).Unwrap();

            return await task;
        }
    }