tags:

views:

59

answers:

4

I pull schedule data from a database via LINQ to SQL and hence use a DataContext object. The data defines when certain actions should be performed and can be updated independently of my service.

I periodically poll the database to see if the schedule has been updated and adjust my scheduling accordingly.

That is, I would, if I knew how to determine if the data was stale.

  • Do I need to create a new DataContext on each check
  • Do I need to implement a custom equality method for the Row type and iterate over the tables?

Ideally I would just do another select using the same data context and get a reference to the table and then call Equals. I realize I live in a dream land.

+2  A: 

Good question!

I found this blog entry on this very question, where someone wanted to use an SqlDependency with LINQ to SQL...

http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2007/05/04/9302.aspx

that would go hand-in-hand with this for WinForms. (You didn't specify WinForms or ASP.NET)

Edit - added

Note that this is only applicable to client apps (WinForms, etc) where there are a very small number of clients, or with an ASP.NET app. The MSDN documentation states this in their documentation at http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldependency.aspx:

SqlDependency was designed to be used in ASP.NET or middle-tier services where there is a relatively small number of servers having dependencies active against the database. It was not designed for use in client applications, where hundreds or thousands of client computers would have SqlDependency objects set up for a single database server.

However, if you are designing an app with only a few users, this should be acceptable.

David Stratton
Why was this a bad answer? I don't mind downvotes if I can learn from them....
David Stratton
It wasn’t a bad answer at all. Rather, StackOverflow is a really bad website: it doesn’t allow me to retract a vote if I wait for too long. Unless the post is edited. Which you did. So I retracted the downvote.
Timwi
Hmm, I was about to recommend Query Notifications because I couldn't remember the class name - and then found that you had already mentioned it. Further reading: http://dunnry.com/blog/UsingSQLDependencyObjectsWithLINQ.aspx
mattdekrey
It looks like this can only be used to notify you upon the execution of a SELECT statementhttp://msdn.microsoft.com/en-us/library/ms181122%28SQL.100%29.aspx
gav
A: 

The autogenerated classes that derive from DataContext are not intended to be long-lived objects. In the vast majority of use-cases, you are supposed to instantiate one, read the data you need and/or make the changes you need, submit, and then forget about the object. It does not directly represent a connection to the database — instantiating lots of DataContexts does not necessarily open multiple connections to the DB.

The consequence of this is that you should consider your data to be always stale, especially if a separate process can access the same database. As soon as you’ve retrieved data, the other process could immediate update that data. The only way to ensure non-staleness is to maintain an open transaction, and as I’m sure you know, the ramifications of keeping a transaction open for too long is that the other process will find the database to be highly unresponsive, as if it were extremely slow — so only use a transaction for a processing step that involves pretty much only DB access that needs to be atomic.

Thus, in your specific case, the only way to find out whether there are any new actions to be performed is to keep asking the database. You don’t have to keep retrieving the data and compare it using Equals; you can just retrieve all the data you haven’t already got, i.e. tell the database to return only the new rows. Since I don’t know your database schema, I’m making things up here, but I’m sure this will give you an idea:

var newActions = db.ScheduledActions.Where(act => !oldActions.Contains(act)).ToList();
if (newActions.Any()) {
    // ... do something intelligent with the new actions ...
    oldActions = oldActions.Concat(newActions);
    // or perhaps oldActions.AddRange(newActions); if oldActions is a List<>
}

Edit: I realise now that you need to check not only for new actions, but also for changes to existing actions. Personally the way I would implement this is by having a date/time field on the ScheduledActions table that specifies the time of the last change. Then you don’t need a custom equality comparer, but instead you can use that to find out what’s changed. Unfortunately, it means that you need to retrieve all the actions, even the ones that haven’t changed. In the following example, I assume that ScheduledActions are identified by a primary key of type string; change that to int if you’re using numeric keys:

Dictionary<string, ScheduledAction> oldActions = new ...;

[...]

var allActions = db.Actions.ToList();
var changedActions = allActions.Where(act => !oldActions.ContainsKey(act.Id) || oldActions[act.Id].LastChanged < act.LastChanged).ToList();
if (changedActions.Any())
{
    // do something intelligent here
}
Timwi
What type is your db object here?
gav
A: 

I have another answer, which is different from my first, but I still think the first is a valid option in limited situations, so I'm posting this separately.

What you're really talking about is handling concurrency issues with LINQ. Microsoft provides guidance on it here:

http://msdn.microsoft.com/en-us/library/bb399373.aspx

David Stratton
A: 

First you need to define very clearely what data (table, column(s)) exactly is subject to your change/update watch and what exactly happens on the DB side when that data is changed. What does "schedule has been updated" mean in your case - one column in a single row changes but it doesn't affect prioritization or a column that affects priotitization changes? Do you have clear separation between data which affects scheduling and data which affects only processing.

Answers to these questions critically determines what is doable even in principle. There is no abstract solution to shceduling problem in general.

Once you determine what's what and what affects what and separate data for real scheduling (determining when something gets processed) from date for processing (determined how something gets processed) you'll reduce your problem to just two points of change and two points of reaction.

Then you can control insert and update on the DB side with sprocs and triggers so that all new things are marked as such. Then you make a sproc that queries just market rows and marks them as "read". When your mid tier recieves them it calls another sproc to acknowledge and that sproc marks them as old. Variations of this depend on actual architecture but this is the crux - marking and updating the state of the data on the spot - meaning in SQL. SQL will also need a trigger to revert records from "read" to "new" if there's no timely acknowledgement (that means your mid-tier died).

Nothing to so with LINQ of DC at all. Once you resolve this you will indeed be able to do equivalent of a single query and receive only items that are new relative to prior readout.

Also, what you receive from LINQ is not a reference to a table in any shape of form - it is a representation of the records received - they will look like table records only if your query doesn't spell out fields that you actually need. When you start using sporcs then it's going to represent whatever the result set of the sproc is - which can be be a join from 5 tables but selecting only 3 fields total.

ZXX