Tracking external changes to a database with LINQ-to-SQL - c#

Is there a way to get SQL Server 2005 to call back to a connected application, such that the connected application will know when a record in a table has had a field modified by another application using the same database?
A simple example would be two instances of the same application connecting to the same table in the same database. When one instance of the application makes a change to a table, the other instance would get a notification that something has changed and be able to query the database for the change.
UPDATE
Thanks so much for the help so far. I would have never even known to look for the SqlDependency class. I've followed the instruction on this page http://msdn.microsoft.com/en-us/a52dhwx7.aspx in creating the SqlDependency test demo. However, I wasn't able to get that to work. I never see the OnChange event get called.
I've also attempted to modify my own application using the instructions as a guide with no luck. I've included the code from my own application below. Basically, the Position table has a PositionID field along with a LocationX and LocationY field. I've written another application that allows me to update the LocationX field of a given row.
What am I missing? Why won't the database changes trigger my even handler?
UPDATE #2
Also note that I am using a hard coded SQL string for my command. I would prefer not to and use the commented out LINQ statement instead. Is it considered OK to use LINQ in this way to generate the SQL string that will be used to build the command?
UPDATE #3
So I managed to figure out what was wrong with my code below. Apparently you have to execute the command once so there will be a cache of data, or else the server doesn't know when to notify you? I added in a line to do a DataAdapter.Fill() with my SqlCommand and the event now seems to fire when expected.
Which brings me to my next problem. The SqlDependency.OnChange event only lets you know that something has changed. How can I figure out from my old DataSet and the new DataSet what the row-by-row changes are?
I could of course, read the entire query again and update all of my data structures, but that seems excessive.
I can call the DataContext.Refresh() and have it do all the updates to my data structures, but that doesn't seem to trigger any of the DataContext generated OnChanging() events. It seems that Refresh() actually tears down all my structures and creates new ones. So I can never figure out what has changed.
Does anyone have any recommendations?
public partial class MainForm : Form
{
private ArpPhase2DbContextDataContext db = null;
private SqlConnection connection = null;
private SqlCommand command = null;
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
this.canRequestNotifications();
this.db = ArpPhase2DbContextDataContext.Instance;
this.setupSqlDependency();
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
SqlDependency.Stop(this.db.Connection.ConnectionString);
if (this.connection != null)
{
this.connection.Close();
}
this.db.SubmitChanges();
}
private bool canRequestNotifications()
{
try
{
SqlClientPermission perm = new SqlClientPermission(PermissionState.Unrestricted);
perm.Demand();
return true;
}
catch
{
return false;
}
}
private void setupSqlDependency()
{
// Remove any existing dependency connection, then create a new one.
SqlDependency.Stop(this.db.Connection.ConnectionString);
SqlDependency.Start(this.db.Connection.ConnectionString);
if (this.connection == null)
{
this.connection = new SqlConnection(this.db.Connection.ConnectionString);
}
if (this.command == null)
{
var sql = (from position in this.db.Positions
select position);
//string commandString = sql.ToString();
string commandString = "SELECT * FROM Positions;";
this.command = new SqlCommand(commandString, connection);
}
this.getData();
}
private void getData()
{
// Make sure the command object does not already have
// a notification object associated with it.
this.command.Notification = null;
// Create and bind the SqlDependency object
// to the command object.
SqlDependency dependency = new SqlDependency(this.command);
dependency.OnChange += new OnChangeEventHandler(this.dependency_OnChange);
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
// This event will occur on a thread pool thread.
// Updating the UI from a worker thread is not permitted.
// The following code checks to see if it is safe to
// update the UI.
ISynchronizeInvoke i = (ISynchronizeInvoke)this;
// If InvokeRequired returns True, the code
// is executing on a worker thread.
if (i.InvokeRequired)
{
// Create a delegate to perform the thread switch.
OnChangeEventHandler del = new OnChangeEventHandler(this.dependency_OnChange);
object[] args = { sender, e };
// Marshal the data from the worker thread
// to the UI thread.
i.BeginInvoke(del, args);
return;
}
// Remove the handler, since it is only good
// for a single notification.
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= this.dependency_OnChange;
// Add information from the event arguments to the list box
// for debugging purposes only.
Console.WriteLine("Info: {0}, Source: {1}, Type: {2}", e.Info.ToString(),
e.Source.ToString(), e.Type.ToString());
// Rebind the dependency.
this.setupSqlDependency();
}
}

SQL Server can do this with Query Notifications. There's nothing built into L2S to support this, but also nothing to stop you from using it outside of L2S in the same app.

Query Notifications uses the indexed view technology to detect data changes and notify subscribed queries when the result set has possibly changed. This is the technology that powers the ASP SqlCacheDependency for cache invalidation. You can read more into how it works at The Mysterious Notification.
In .Net Framework the most comonly used component that leverages Query Notifications is SqlDependency. There are various samples on how to integrate linq2sql with SqlDependency, like linqtosqlcache.
You should not use this technology to watch for data that changes frequently but solely for catalog reference data that is worth caching. The cost of setting up and delivering the notification is significant.

Why do you want to do that?
Linq-to-SQL was dead before you began using it.
Now they push EF, WCF-DS etc. (who knows when they will kill them too).
Even query notifications are not a safe bet anymore (as they are so fragile if you have an app that is going to last more than a few years).

Related

How to wait for some operation in another class to start next operation in c#?

I know you don't understand the question title. Let me tell you the whole scenario.
I have a class named as Processor. Processor can get notifiable steps or send notification to other application API depending on some condition. Like as below:
Method: SendOrStartProcess(Operation operation)
Method Implementation:
If(operation.steps.any(o=>o.canProcess)){
_service.notify(operations); //This is fine
}
else{
var totalSteps = await _service.getAdjustableSteps(); //It returns the adjustable steps and will invoke event which is subscribed by another class named as GetAdjustableStepsEnd. It can be invoked immediately or after some time.
//Set totalSteps in operationsc.steps and save in database and that's it.
}
Now, I have another class named as "OperationHandler" which subscirbed the GetAdjustableStepsEnd event.
public OperationHandler(IUnityContainer container)
{
_agent.GetAdjustableStepsEnd += GetAdjustableStepsEnd ;
}
public async void GetAdjustableStepsEnd (object sender, GetAdjustableStepsEndEventArgs e)
{
// Here i will call again the above method _processor.SendOrStartProcess(e.Operations);
}
//Now the problem is if event invokes after some time then it is fine because meanwhile i set the status in database. But if it invoked just after the getAdjustableSteps then i call SendOrStartProcess again and it sends getAdjustableSteps again because record is not set in the database. How to overcome this situation. I can not put lock on it because this is used by many clients.

Winforms Datagridview can't be refreshed from a delegate

I'm trying to load data from file to list and show that data immediately on Winforms' Datagridview. For that I've made the reading in another thread using Backgroundworker. The problem is, it only updates once and I can't get it to show more data. Not only that, when clicked, it tries to access element with -1 index, which of course doesn't exist, resulting in a crash.
Usually, from what I've seen, simply adding again same data to data source dataGridView1.DataSource = samelist; should work, but not in this case.
BackgroundWorker's work
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
//lotsofCode...
while (readData != null)
{
fooLists.Add(readData);
//someCalculations...
worker.ReportProgress();
}
}
BackgroundWorker's progressChanged
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.Invoke((MethodInvoker)delegate { UpdateGridView(); });
}
UpdateGridView Method
private void UpdateGridView()
{
if (fooLists.GetListById(1).calculatedList != null)
dataGridView1.DataSource = fooLists.GetListById(1).calculatedList;
}
Later on I've read some threads on stack, where one suggested using BindingSource as a "middleman", so now I have dataGridView1.DataSource = MyBindingSource; in the component initialization and tab1source.DataSource = fooLists.GetListById(1).calculatedList; instead of dataGridView1.DataSource. It certainly helped, as the list is now clickable as it should be, but still there are only few records on a list.
None of dataGridView1.Refresh(), dataGridView1.RefreshEdit() or dataGridView1.Update() helped, though made the list loading slightly fancier (probably due to the delay they introduced :) ).
I tried making some "protections" (semaphores, so the delegate isn't called again, while working; try-catches, though no exceptions are thrown there; data clearing before re-writing...) but the "better version" worked as poor as this one and it only darkened the code.
Am I missing a way to update the Datagridview control? Thanks in advance.
Although you didn't write it, but I think the reason that the items that you add to your dataSource are added to a collection that does not implement interface IBindingList. You probably use a simple list to hold your read data.
If your 'DataSourceimplements this interface, then after adding an item to your collection an event is raised. The class that holds theDataSource, whether it is aDataGridViewor aBindingSource` get notified about the changes in the list and update their contents accordingly.
Your solution would be to store your elements in an object of class System.ComponentModel.BindingList<T>.
Suppose the items you want to show are of class MyReadData
class MyForm : Form
{
public MyForm()
{
InitializeComponents();
this.myReadItems = new BindingList<MyReadData>();
this.MyBindingSource.DataSource = this.myReadItems;
// if not already done in InitializeComponents
this.MyDataGridView.DataSource = this.MyBindingSource;
}
private readonly BindingList<MyReadData> myReadItems;
// whenever needed, start the BackGroundWorker.
private void OnButtonReadFile_Click(object send, EventArgs e)
{
// create and start the backgroundworker
BackGroundWorkdr worker = ...
MyBackGroundWorkerParams params = ...
worker.RunWorkerAsync(params);
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
// I am certain the sender is my BackGroundWorker:
BackgroundWorker worker = (BackGroundWorker)sender;
MyBackGroundWorkerParams params = (MyBackGroundWorkerParams)e.Argument;
// do some work using the params
while (readData != null)
{
// some data read.
// dont't add the data to the list, just report the data that must been added to the list:
// someCalculations...
int percentProgress = ...
MyReadData dataToAddToGrid = ...
worker.ReportProgress(percentProgress, dataToAddToGrid);
}
private void bw_progressChanged(object sender, ProgressChangedEventArgs e)
{
// no need to call invoke, this is already the context of your forms thread
Debug.Assert(!This.InvokeReguired);
MyReadData dataToAdddToGrid = (MyReadData)e.UserState;
this.myReadItems.Add(dataToAddToGrid);
}
}
The main difference is that you should not let your BackgroundWorker to add data to the list of displayed data. The task of the BackGroundWorker is to read the data and to report to everyone who is interested what data has been read.
As it is the task of MyForm to display the read data, let MyForm decide which read data to display and in what format. This enhances reusage of both MyForm and MyBackGroundWorker: MyForm could display that that has been fetched in a different way, and MyBackGroundWorker could be used to inform others than MyForm to notify about read data.
Furthermore the display context of the progress changed event handler is the context of 'MyForm`, so an invoke is not needed.
You could also assign the IBindingList directly to the DataGridView, so without the use of a BindingSource. The only reason to keep a BindingSource is if you want access to the Current item, or if you want the freedom to fill your DataGridView with other items than the contents of your BindingList.
Finally: the most important part of the solution was that the items were added to an IBindingList.
System.Components.BindingList<T> is a class with limited functionality. If you want to order the rows in your DataGridView, or only show items that match some predicate, or combine items from several sources into one DataGridView, consider using Equin.ApplicationFramework.BindingListView
using Equin.ApplicationFramework;
public MyForm()
{
InitializeComponents();
this.myReadItems = new BindingListView<MyReadData>(this.components);
this.MyBindingSource.DataSource = this.myReadItems;
this.MyDataGridView.DataSource = this.MyBindingSource;
}
private readonly BindingListView<MyReadData> myReadItems;
private void bw_progressChanged(object sender, ProgressChangedEventArgs e)
{
MyReadData dataToAdddToGrid = (MyReadData)e.UserState;
this.myReadItems.Add(dataToAddToGrid);
// finished updating the list, DataGridView can be updated:
this.myReadItems.Refresh();
// this Refresh function allows you to change several items in the list
// without unnecessary intermediate updates of your BindingSource and DataGridView
}
Presto, that is all: free sorting or your columns by clicking on the column header. Consider examining their example project to see how filtering works and how to use several sources.
// Show only Brummies
this.myReadData.ApplyFilter(person => person.Address.City == "Birmingham");
// Remove the filter, show everyone again
this.myReadData.RemoveFilter();

SCVMM 2012 console addin execution

I'm developing an SCVMM 2012 console Add-In.
The SDK documentation can be found here: http://msdn.microsoft.com/en-us/library/jj860311.aspx
But the documentation has no info on threading or how the add-in gets executed at all.
Now here's what I have:
public class SomeAddIn : ViewAddInBase
{
private bool gotServerInfo = false;
private bool gotConnectionString = false;
public override FrameworkElement CreateViewControl()
{
GetServerInfo();
GetConnectionString();
if(gotServerInfo && gotConnectionString)
{
return GetGoodFrameworkElement(); //do some stuff to fill FrameworkElement
}
MessageBox.Show("Can't connect to DB, returning empty screen...");
return new FrameworkElement();
}
private void GetServerInfo()
{
PowerShellContext.ExecuteScript<ServerConnection>("Get-SCVMMServer localhost",
(items, error) =>
{
// code to set server info here
if (error == null)
{
gotServerInfo = true;
MessageBox.Show("Got settings from server.");
}
else{//Error}
});
}
private void GetConnectionString()
{
//PowerShell connect to database, get connection string
gotConnectionString = true; //if got string
}
}
Looks all good, but the problem is that powershell commands take time to execute and the "return new FrameworkElement();" gets executed first before gotServerInfo and gotConnectionString get set to true.
My guess is that VMM starts multiple threads for my methods and the execution of those is not sequential anymore. How do I get VMM to execute my methods in the right order?
What I've tried to do:
1) use threads for my methods, set priority to high, set current
thread priority to low or even as background, yet this doesn't help.
Thread.Join doesn't work either.
2) move my methods to "public override void OnLoad() or
OnShow(). The CreateViewControl() gets executed first anyway.
Any ideas?
Not a solution to the problem occurring during initialization, but I moved on to MVVM (WPF) and a separate threading model. After you get the basic interface up, it's up to you what you want to do and how to synchronize everything. VMM latter acts as a usual WPF application giving you one main thread for the interface. Use ThreadPool class for dispatching background tasks - found it's the easiest way to do it.

SQL Dependency not Firing

I've tried setting up an SQL Dependency to fire on a "Count Rows" query (written in C#, SQL Server 2008 Express), but after the original subscription SQLNotificationType goes, the event handler never seems to want to fire again (despite rows being added, and I've checked the SQL and it's returning the expected value...).
My code is below. Any thoughts are much appreciated!
EDIT: The project that this code is in is a WPF program. I have this particular code stored in a separate class, which my WPF program creates an instance of in an 'Initialized' event handler. I then have a method in this class that basically calls ConnectToDatabase() first, and then calls SetupSQLDependency().
EDIT 2: As a side note, this program is a WPF which I was hoping to distribute to a few users. The goal was to have the WPF update with certain information whenever new rows were added to a database. I thought that this would be the best way to go about it, as opposed to always querying the database.
private void ConnectToDatabase()
{
//This method is the first to be called, and is the entry
// point into my SQL database code.
databaseConnection = new SqlConnection(connectionString);
// Setup command used in SqlDependecy
SqlCommand tempCmd = new SqlCommand();
tempCmd.Connection = databaseConnection;
tempCmd.CommandText = "SELECT COUNT(ID) FROM [Example].[dbo].[ExampleTable]";
sqlCmd = tempCmd;
try
{ databaseConnection.Open(); }
catch (Exception e)
{ writeDebug(e.ToString()); }
}
private void SetupSQLDependency()
{
SqlDependency.Stop(connectionString);
SqlDependency.Start(connectionString);
sqlCmd.Notification = null;
// create new dependency for SqlCommand
SqlDependency sqlDep = new SqlDependency(sqlCmd);
sqlDep.OnChange += new OnChangeEventHandler(sqlDep_OnChange);
SqlDataReader reader = sqlCmd.ExecuteReader();
}
private void sqlDep_OnChange(object sender, SqlNotificationEventArgs e)
{
// FROM: http://msdn.microsoft.com/en-us/a52dhwx7.aspx
if (e.Type == SqlNotificationType.Change)
{
//++++++ THIS IS THE BLOCK THAT IS NEVER TRIGGERED ++++++//
// Have to remove this as it only work's once
SqlDependency sqlDep = sender as SqlDependency;
sqlDep.OnChange -= sqlDep_OnChange;
// Resetup Dependecy
SetupSQLDependency();
}
else if (e.Type == SqlNotificationType.Subscribe)
{
double te = 12; // Used this just to test a break... code is useless
}
}
I believe the problem here is the COUNT. Refer to the MSDN documentation for Supported SELECT Statements for more info:
The projected columns in the SELECT statement may not contain aggregate expressions unless the statement uses a GROUP BY expression. When a GROUP BY expression is provided, the select list may contain the aggregate functions COUNT_BIG() or SUM(). [...]

How to run a function on a background thread for Windows Phone 7?

I'm using MVVM Light to build a WP7 (Windows Phone 7) application. I wish to have all the work performed by the Model to be run on a background thread. Then, when the work is done, raise an event so that the ViewModel can process the data.
I have already found out that I cannot invoke a Delegate asynchronously from an WP7 app.
Currently I am trying to use ThreadPool.QueueUserWorkItem() to run some code on a background thread and use MVVM Light's DispatcherHelper.CheckBeginInvodeOnUI() to raise an event on the UI thread to signal the ViewModel that the data has been loaded (this crashes VS2010 and Blend 4 when they try to display a design-time view).
Is there any sample code to run some code on a background thread and then dispatch an event back to the UI thread for a WP7 app?
Thanks in advance,
Jeff.
Edit - Here is a sample Model
public class DataModel
{
public event EventHandler<DataLoadingEventArgs> DataLoadingComplete;
public event EventHandler<DataLoadingErrorEventArgs> DataLoadingError;
List<Data> _dataCasch = new List<Data>();
public void GetData()
{
ThreadPool.QueueUserWorkItem(func =>
{
try
{
LoadData();
if (DataLoadingComplete != null)
{
//Dispatch complete event back to the UI thread
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
//raise event
DataLoadingComplete(this, new DataLoadingEventArgs(_dataCasch));
});
}
}
catch (Exception ex)
{
if (DataLoadingError != null)
{
//Dispatch error event back to the UI thread
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
//raise error
DataLoadingError(this, new DataLoadingErrorEventArgs(ex));
});
}
}
});
}
private void LoadData()
{
//Do work to load data....
}
}
Here's how I'd approach a solution to this.
Your ViewModel implements INotifyPropertyChanged right? There's no need to dispatch the Events. Just raise them "bare" in the Model, then dispatch the RaisePropertyChanged in the ViewModel.
And yes, you should have some sort of singleton model/database in your code. After all, what is a SQL Database if not some gigantic singleton? Since we don't have a database in WP7, don't be shy creating a singleton object. I have one called "Database" :)
I've just tried threading my dataloads in there, and realise that in fact the best approach is simply implementing INotifyPropertyChanged right down at the model level. There's no shame in this.
So given that, here's what I'm doing in the singleton Database object to load and return my Tours "table" (note the thread.sleep to make it take a visible amount of time to load, normally its sub 100ms). Database class now implements INotifyPropertyChanged, and raises events when loading is completed:
public ObservableCollection<Tour> Tours
{
get
{
if ( _tours == null )
{
_tours = new ObservableCollection<Tour>();
ThreadPool.QueueUserWorkItem(LoadTours);
}
return _tours;
}
}
private void LoadTours(object o)
{
var start = DateTime.Now;
//simlate lots of work
Thread.Sleep(5000);
_tours = IsoStore.Deserialize<ObservableCollection<Tour>>( ToursFilename ) ?? new ObservableCollection<Tour>();
Debug.WriteLine( "Deserialize time: " + DateTime.Now.Subtract( start ).ToString() );
RaisePropertyChanged("Tours");
}
You follow? I'm deserializing the Tour list on a background thread, then raising a propertychanged event.
Now in the ViewModel, I want a list of TourViewModels to bind to, which I select with a linq query once I see that the Tours table has changed. It's probably a bit cheap to listen for the Database event in the ViewModel - it might be "nicer" to encapsulate that in the model, but let's not make work we we don't need to eh?
Hook the Database event in the Viewmodel's constructor:
public TourViewModel()
{
Database.Instance.PropertyChanged += DatabasePropertyChanged;
}
Listen for the appropriate table change (we love magic strings! ;-) ):
private void DatabasePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "Tours")
{
LoadTourList();
}
}
Select the records I want from the table, then tell the view there is new data:
public void LoadTourList()
{
AllTours = ( from t in Database.Instance.Tours
select new TourViewModel( t ) ).ToList();
RaisePropertyChanged( "AllTours" );
}
And lastly, in your ViewModelBase, it's best to check if your RaisePropertyChanged needs dispatching. My "SafeDispatch" method is pretty much the same as the one from MVVMlight:
private void RaisePropertyChanged(string property)
{
if ( PropertyChanged != null )
{
UiHelper.SafeDispatch(() =>
PropertyChanged(this, new PropertyChangedEventArgs(property)));
}
}
This works perfectly in my code, and I think is fairly tidy?
Lastly, extra for experts: in WP7, it might be good to add a ProgressBar with IsIndeterminate=True to your page - this will display the "dotted" progress bar. Then what you can do is when the ViewModel first loads you could set a "ProgressBarVisible" property to Visible (and raise the associated PropertyChanged event). Bind the ProgressBar's visibility to this ViewModel property. When the Database PropertyChanged event fires, set the visibility to Collapsed to make the progressbar go away.
This way the user will see the "IsIndeterminate" progress bar at the top of their screen while the deserialization is running. Nice!
I have not developed for WP7 before, but I found this article that might be useful!
Here is the Dining Philosopher sample code from the article that should give you a good idea on how to raise an event to the UI from another thread:
public DinnersViewModel(IDinnerCatalog catalog)
{
theCatalog = catalog;
theCatalog.DinnerLoadingComplete +=
new EventHandler<DinnerLoadingEventArgs>(
Dinners_DinnerLoadingComplete);
}
public void LoadDinners()
{
theCatalog.GetDinners();
}
void Dinners_DinnerLoadingComplete(
object sender, DinnerLoadingEventArgs e)
{
// Fire Event on UI Thread
View.Dispatcher.BeginInvoke(() =>
{
// Clear the list
theDinners.Clear();
// Add the new Dinners
foreach (Dinner d in e.Results)
theDinners.Add(d);
if (LoadComplete != null)
LoadComplete(this, null);
});
}
I hope it's helpful :).
One thing that's confusing: you said that when you use the helper to raise the event, then VS2010 crashes... what exactly are you seeing when it's crashing? Are you getting an exception?
Jeff, I'm still figuring this stuff out myself. I posted a similar question and ended up answering it myself by building a simple sample. Here:
A super-simple MVVM-Light WP7 sample?
The summary is:
1) I derived my Model (yes my model) from ViewModelBase. This gives me Mvvm-Light's implementation of messaging and INotifyPropertyChanged which is handy. You could argue this is not "pure" but I don't think it matters.
2) I used Mvvm-Light DispatcherHelper.CheckBeginInvokeOnUIhelper just as you did (from my Model, NOT my ViewModel).
Hope this helps.

Categories

Resources