Cache and SqlCacheDependency (ASP.NET MVC)

2k views Asked by At

We need to return subset of records and for that we use the following command:

using (SqlCommand command = new SqlCommand(
                    "SELECT ID, Name, Flag, IsDefault FROM (SELECT ROW_NUMBER() OVER (ORDER BY @OrderBy DESC) as Row, ID, Name, Flag, IsDefault FROM dbo.Languages) results WHERE Row BETWEEN ((@Page - 1) * @ItemsPerPage + 1) AND (@Page * @ItemsPerPage)",
                    connection))

I set a SqlCacheDependency declared like this:

SqlCacheDependency cacheDependency = new SqlCacheDependency(command);

But immediately after I run the command.ExecuteReader() instruction, the hasChanged base property of the SqlCacheDependency object becomes true although I did not change the result of the query in any way! And, because of this, the result of this query is not kept in cache.

HttpRuntime.Cache.Insert( cacheKey, list, cacheDependency, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(AppConfiguration.CacheExpiration.VeryLowActivity));

Is it because the command has 2 SELECT statements? Is it ROW_NUMBER()? If yes, is there any other way to paginate results?

Please help! After too many hours, a little will be greatly appreciated! Thank you

3

There are 3 answers

1
rshadman On BEST ANSWER

Running into the same issue and finding the same answers online without any help, I was reasearching the xml invalid subsicription response from profiler.

I found an example on msdn support site that had a slightly different order of code. When I tried it I realized the problem - Don't open your connection object until after you've created the command object and the cache dependency object. Here is the order you must follow and all will be good:

  1. Be sure to enable notifications (SqlCahceDependencyAdmin) and run SqlDependency.Start first
  2. Create the connection object
  3. Create the command object and assign command text, type, and connection object (any combination of constructors, setting properties, or using CreateCommand).
  4. Create the sql cache dependency object
  5. Open the connection object
  6. Execute the query
  7. Add item to cache using dependency.

If you follow this order, and follow all other requirements on your select statement, don't have any permissions issues, this will work!

I believe the issue has to do with how the .NET framework manages the connection, specifically what settings are set. I tried overriding this in my sql command test but it never worked. This is only a guess - what I do know is changing the order immediately solved the issue.

I was able to piece it together from the following to msdn posts.

This post was one of the more common causes of the invalid subscription, and shows how the .Net client sets the properties that are in contrast to what notification requires.

https://social.msdn.microsoft.com/Forums/en-US/cf3853f3-0ea1-41b9-987e-9922e5766066/changing-default-set-options-forced-by-net?forum=adodotnetdataproviders

Then this post was from a user who, like me, had reduced his code to the simplest format. My original code pattern was similar to his.

https://social.technet.microsoft.com/Forums/windows/en-US/5a29d49b-8c2c-4fe8-b8de-d632a3f60f68/subscriptions-always-invalid-usual-suspects-checked-no-joy?forum=sqlservicebroker

Then I found this post, also a very simple reduction of the problem, only his was a simple issue - needing 2 part name for tables. In his case the suggestion resolved the issue. After looking at his code I noticed the main difference was waiting to open the connection object until AFTER the command object AND the dependency object were created. My only assumption is under the hood (I have not yet started reflector to check so only an assumption) the Connection object is opened differently, or order of events and command happen differently, because of this association.

https://social.msdn.microsoft.com/Forums/sqlserver/en-US/bc9ca094-a989-4403-82c6-7f608ed462ce/sql-server-not-creating-subscription-for-simple-select-query-when-using-sqlcachedependency?forum=sqlservicebroker

I hope this helps someone else in a similar issue.

1
LukeH On

Just a guess, but could it be because your SELECT statement doesn't have an ORDER BY clause?

If you don't specify an explicit ordering then it's possible for the query to return the results in any order each time it is run. Maybe this is causing the SqlCacheDependency object to think that the results have changed.

Try adding an ORDER BY clause:

SELECT ID, Name, Flag, IsDefault
FROM
(
    SELECT ROW_NUMBER() OVER (ORDER BY @OrderBy DESC) AS Row,
        ID, Name, Flag, IsDefault
    FROM dbo.Languages
) AS results
WHERE Row BETWEEN ((@Page - 1) * @ItemsPerPage + 1) AND (@Page * @ItemsPerPage)
ORDER BY Row
1
shockstump On

i'm no expert on SqlCacheDependency, in fact, i found this question whilst looking for answers to my own issues with it! However, i believe the reason your SqlCacheDependency is not working is because your SQL contains a nested sub query.

Take a look at the documentation which lists what you can/can not use in your SQL: Creating a Query for Notification

"....The statement must not contain subqueries, outer joins, or self-joins....."

I also found some invaluable troubleshooting info from a guy at Redgate here: Using and Monitoring SQL 2005 Query Notification that helped me solve my own problem: By using Sql Profiler to trace the QN events he suggests, i was able to spot my connection was incorrectly using the 'SET ARITHABORT OFF' option, causing my notifications to fail.