Canceling DataAdapter.Fill() - c#

Scenario:
We have a DataGridView which is attached to DataAdapter (datatable), we load the data in datatable using (adapter.fill(query, datatable)) in a separate thread (using delegate and beginInvoke) and once the data is loaded we attached that datatable to datagridview (in the main thread)
Is there a way we can check if fill() is still executing and cancel it.
Real scenario:
User click on the user name and corresponding data is loaded in the datagrid. Sometime, user is impatient and click on the another user (here I want to cancel the previous fill and start a new fill)
UPDATE:
We keep two DataApdaters (and two DataTables) and we attach one datatable to datagridview and start loading data to another datatable asynchronously. When data is loaded we simply bind the datagridview to DataTable which we just filled (and start loading the previous datable asynchronously) This way UI will always get the current data (without user waiting on UI to refresh or hang)

You can provide a SqlCommand to adapter constructor and invoke a Cancel method on it.
There is a raw template :
class Model
{
private SqlCommand loadUserCommand;
private DataTable userData;
public void LoadUser(string userId)
{
loadUserCommand = GetUserLoadCommandForUserID(userId);
userData = new DataTable("userData");
using (var adapter = new SqlDataAdapter(loadUserCommand))
{
adapter.Fill(userData);
}
}
public void AbortLoadUser()
{
if (loadUserCommand!= null)
loadUserCommand.Cancel();
}
private SqlCommand GetUserLoadCommandForUserID(string userId)
{
var connection = new SqlConnection("...");
var command = connection.CreateCommand();
...
}
}

There is no facility to safely cancel DataAdapter.Fill().
To work around this, one option would be to implement a mechanism that can cause these unwanted fills to be ignored and so not reflected in the UI. I would recommend incrementing a counter at the beginning of an async operation and passing that state to your asyn action. Your async action can then check its counter value against the current counter when it finishes. If the counters are not equal, then do not update the UI.
If you see a pattern where the user rapidly clicks between users and lots of requests get discarded, then implement a timer mechanism whereby you only retrieve data if the user stays on the selection for a minimum amount of time.

I did a quick search and found this: cancel a DataAdapter.Fill There seems to be no way to get around handling an exception, as the author of the code states.

Related

Update ObservableCollection from background Worker

I have a DatGrid, which is bound to var Result_Full = new ObservableCollection<IP_DataRow>(). This is a simple class containing several string & double variables. Nothing difficult.
What I do, is that I read an Excel File (with Telerik RadSpreadProcessing), which parses rows into my class. I do this on a thread so that the UI is not blocked. I have encountered a few problems though:
1) I cannot use ref keyword in a long process which reads excel file (because Result_Full is a public property bound to a DataGrid), but I have to create temporary ObservableCollection<IP_DataRow>(), where the values are placed. Once the process has finished I run the following script for copying the values:
foreach (var item in tmpFull)
{
InvokeOnUIThread(() =>
{
Result_Full.Add(item);
});
}
What I would like to do, is to be able to see in a real time (if possible) how items are being added to the collection in my DataGrid.
As I am using .Net 4.5 I tried to implement BindingOperations.EnableCollectionSynchronization as was suggested by some other post, yet I could not figure out how to bind my UI bould collection Result_Full to temporary used in a process.
2) Even with the current setup, when (under my UI) I move to my Tab which contains DataGrid (my DataGrid is on a different TabPage), and I try to add new item to the collection with the above mentioned code, it returns an error saying: The calling thread cannot access this object because a different thread owns it., which is rather weird, as InvokeOnUIThread is nothing else but Dispatcher.Invoke(), which should be thread safe?
Any help would be highly appreciated.
EDIT: Showing more code:
This is the process I call from BackgroundWorker:
public void ProcessFile()
{
var tmpError = new ObservableCollection<IP_DataRow>();
var tmpFull = new ObservableCollection<IP_DataRow>();
var _reader = new IP_ExcelReader(sExcelPath, ref tmpError, ref tmpFull);
string sResult = _reader.ReadExcelFile();
if (sResult != string.Empty)
{
System.Windows.MessageBox.Show("Error processing selected Excel File!" + Environment.NewLine + Environment.NewLine + "Error message:" + Environment.NewLine + sResult);
}
foreach (var item in tmpError)//populates error list
{
IP_InvokeOnUIThread(() =>
{
Result_Error.Add(item);
});
}
foreach (var item in tmpFull)//populates full list
{
IP_InvokeOnUIThread(() =>
{
Result_Full.Add(item);
});
}
OnPropertyChanged("Result_Full");
//OnPropertyChanged("Result_Error");
iSelectedTabIndex = 1;
}
Here you can see, that I have to create temporary collection tmpError, tmpFull where I gather my data. At the end of process, I manually copy values into my main collections bound to DataGrid. I would like to change this, meaning that values are copied to the main collection (not temporary ones) during the process, so that user can see in a real time how values are added to the collection.
P.S.2:
for uknown reason to me, one of the problems lied in my InvokeOnUIThread call. Once I changed from App.Current.Dispatcher.Invoke(action); to App.Current.Dispatcher.BeginInvoke(action); error with ..different thread owns it stopped.
You can use BackgroundWorker instead of thread, to report progress as it goes.
Here is a simple tutorial
I believe that simply calling Dispatcher will use thread of a context, which is not UI-thread in your case. Try Application.Current.Dispatcher instead
In short, I believe you should do the following:
Create public ObservableCollection in UI-thread and bind it to DataGrid
Create a background worker. Set reporting to true. Subscribe to ReportProgress and DoWork events.
Run worker async
In DoWork handler create a list and read some amount of values to it. As you reach some amount, let's say a hundred, call (sender as BackgroundWorker).ReportProgress method, passing in event args this collection you have populated.
In report progress handler, populate your ObservableCollection from a list you've passed throught event args.
Steps 4 - 5 are repeated until everything is done

SQLDependency + Service Broker

I'm using SqlDependency to get notification when data in some table are changed.
private void subscribeBroker()
{
using (var conn = new SqlConnection(connString))
{
conn.Open();
var cmd = new SqlCommand("SELECT text FROM dbo.Test");
cmd.Connection = conn;
var dependency = new SqlDependency(cmd);
dependency.OnChange += dependency_OnChange;
SqlDependency.Start(connString);
cmd.ExecuteNonQuery();
}
}
void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
//Do something...
subscribeBroker();
}
It is working but I have some questions.
1) I didn't find a way how to get information which row was changed. I need to read all data from entire table to see what is different. Is there a way how to get this information? (primary ID, or something) Maybe to use different approach than SqlDependency?
2) What if "somebody" changing data very fast. It is possible that some changes will not being notified? (I'm concerned about time between notification and time when I subscribe it again.
Thank you.
About 1- query notification informs you about the fact, that something is changed. If you want to get what was changed since last time- you could probably use timestamp column.
About 2- query notification informs you about changes and then is dropped. then you again subscribe for notification again. that mean- time between dropping and creation of notifications is that time in which notification about changes is not send.
Query notifications is more for the situations, when your data is not changing frequently. For example- some cashed classification values. So- you subscribe for changes in some table, wait for changes and at the time they happen you get latest version of data. Should consider that query notification also uses server resources, so if you have huge table and want to get changes on some small subset of data, a lot of queries can be affected in terms of performance (something like indexed view).
If you need to take some action based on changed data and each change is important, then i would guess that trigger + service broker could be more effective. Or, depending on your needs, Change Data Capture.

Where to store progress information in ASP.Net web application

I'm creating a page that get uploaded text files and builds them into multiple PDFs. They are just exports from Excel. Each row in the file corresponds to a new PDF that needs to be created.
Anyway, once the files are uploaded I want to begin processing them, but I don't want the user to have to stay on the page, or even still have their session open. For example they could close the browser and come back 10 minutes later, log in, and the progress information will say like 112/200 files processed or something. It will be a lot quicker than that though.
So two questions really, how can I pass this processing job to something (Handler?Thread?) that will continue to run when the page is closed, and will return as soon as the job has started (so the browser isn't stopped)? Secondly, where can I store this information so that when the user comes back to the page, they can see the current progress.
I realise that I can't use sessions, and since it will be processing about a file a second I don't really want to update a DB every second. Is there some way I can do this? Is it possible?
I solved this by using the link provided by astander above. I simply create an object in the HttpContext.Application to store progress variables, and then Set the method which does my processing inside a new Thread.
// Create the new progress object
BatchProgress bs = new BatchProgress(0);
if(Application["BatchProgress"] != null)
{
// Should never happen
Application["BatchProgress"] = bs;
}
else
{
Application.Add("BatchProgress","bs");
}
//Set up new thread, run batch is the method that does all the processing.
ThreadStart ts = new ThreadStart(RunBatch);
Thread t = new Thread(ts);
t.Start();
It then returns after the thread starts and I can use jQuery to get the Application["BatchProgress"] object at regular intervals. At the end of my thread the BatchProgress object has its status set to "Complete", then when jQuery queries it, it sees the complete status and removes the progress object from the application.

When is SqlCommand.StatementCompleted supposed to fire?

Update Do I have to clarify something in my question? I'm amazed to see I didn't get any rating, comment or answer in two weeks time.
I'm trying to write a simple winforms application that executes a SQL SELECT statement asynchronous. When the sql server starts returning results, I want to execute an event handler I've wired up to the SqlCommand's StatementCompleted event.
The form contains two buttons, a textbox, and a label. When button1 is clicked, I create the SqlCommand and wire up the event handler, then I open the SqlConnection and call BeginExecuteReader in order to start the asynchronous operation. I set my label to show the command is executing.
In the event handler, I simply set the label to show the command is finished.
When button 2 is clicked, I change the label to show we're processing the results. Then I call EndExecuteReader and assign its return value to a new SqlDataReader which I then process.
What I see is that the event handler doesn't get called when the command is ready. In stead, it gets called when my code finishes processing the reader returned by EndExecuteReader.
Am I missing something here? Do I misinterpret the intended use of the event? I've tried to find an example of StatementCompleted, but I could only find general descriptions of it, no working code. The example at the SqlCommand.BeginExecuteReader page at MSDN uses a loop and waits for the IAsyncResult.IsCompleted property to be true. I would expect that at the same time that property gets true, the StatementCompleted event fires.
public Form1() {
InitializeComponent();
}
private IAsyncResult iAsyncResult;
private SqlCommand sqlCommand;
private void statementCompleted(object sender,
StatementCompletedEventArgs e) {
label1.Text = "Statement completed";
}
private void button1_Click(object sender, EventArgs e) {
var northWindConnection =
new SqlConnection(
"Data Source=.\\SqlExpress;Initial Catalog=Northwind;" +
"Integrated Security=True;" +
"asynchronous processing=true");
sqlCommand = new SqlCommand("WAITFOR DELAY '00:00:05';" +
" SELECT * FROM [Order Details]",
northWindConnection);
sqlCommand.StatementCompleted += statementCompleted;
northWindConnection.Open();
iAsyncResult = sqlCommand.BeginExecuteReader();
label1.Text = "Executing";
}
private void button2_Click(object sender, EventArgs e) {
label1.Text = "Not waiting anymore, reading";
var results = new StringBuilder();
var reader = sqlCommand.EndExecuteReader(iAsyncResult);
while (reader.Read()) {
for (int i = 0; i < reader.FieldCount; i++) {
results.Append(reader[i].ToString() + "\t");
}
results.Append(Environment.NewLine);
}
reader.Close();
sqlCommand.Connection.Close();
textBox1.Text = results.ToString();
}
The sequence of events is this:
Call SqlCommand.BeginExecuteReader(callback, stateObject) sends the T-SQL to SQL Server and the command starts executing.
When data is first available, the AsyncCallback provided to BeginExecuteReader() is called.
The callback invokes EndExecuteReader() to obtain a reference to a SqlDataReader object.
You use the SqlDataReader to read the results of the query. This could be one row, or millions of rows. The query is not complete until all data requested has been returned.
Repeat for additional result sets, if any.
Invoke the StatementCompleted event -- but only if the query / stored procedure did not use SET NOCOUNT ON.
In other words, StatementCompleted is called when the T-SQL has completely finished, including all associated data transfers.
Adding this for anyone that might run across this question since it was asked months ago with no answers provided.
The StatementCompleted event isn't useful in applying an async call pattern against SqlCommand. It does get fired but only during the call to EndExecuteReader which is basically too late. If you want to implement an async call pattern in general, this MSDN article has an excellent explanation of how it can be done. The sample code in the BeginExecuteReader documentation shows the correct usage of SqlCommand in an async mode.
I suspect the clue to this behaviour is that the event's "StatementCompletedEventArgs" parameter includes the property "RecordCount" which is the number of rows affected by a statement.
MS SqlServer (and before that Sybase SqlServer, as it then was) returns the number of rows affected as a separate "message" (using the term loosely) after the actual data has all been sent.
Also, beware: A Sql Command can consist of a number of SQL Statements, each of which can affect a number of rows and therefore return a number of "rows affected". I would therefore assume the event might fire several times for a given SQL Command; or no times at all is SET NOCOUNT ON was used.

How to make my Silverlight application get displayed after I loaded the data?

I have a SL application that reads some data from the database, which I do through a WCF Service. I was having some delay problems due to this access, so I solved it by loading all the data into a dictionary in my app. Now I want to make sure the application will only be displayed after I loaded all this information, how can I do that? I thought that just puting my InitializeComponents after the data load would be enough, but it is not. Here is some piece of my code:
public Brasil()
{
//InitializeComponent();
webService = new DataRetrieverReference.DataRetrieverClient();
webService.GetCounterCompleted += new EventHandler<WebPortos.DataRetrieverReference.GetCounterCompletedEventArgs>(webService_GetCounterCompleted);
webService.GetCounterAsync();
webService.GetDataCompleted += new EventHandler<DataRetrieverReference.GetDataCompletedEventArgs>(webService_GetDataCompleted);
}
void webService_GetCounterCompleted(object sender, WebPortos.DataRetrieverReference.GetCounterCompletedEventArgs e)
{
int counter = e.Result;
this.dictionary = new Dictionary<int, WebPortos.DataRetrieverReference.vwPortos_SEP>();
for (int i = 0; i < counter; i++)
{
webService.GetDataAsync(i);
}
InitializeComponent();
}
As you can see, I put it inside my data loading method, but it didn't work. Any tips?
It is bad practice to delay the creation of views in any way. Leave InitializeComponent in the constructor! :)
What you want to do is simply hide your display until the data is ready. Easiest way (to keep this example simple) is start with Visibility set to collapsed on some parent element on the page, then set that to Visible again after the data is loaded.
Real world solutions involve using busy indicators to stop interaction with specific areas/controls while the data loads.
Looking at your code the GetDataAsync is making an async request for each set of data and then performing the InitializeComponent afterwards. So you would need to delay the InitializeComponent call until after all the GetDataCompleted callbacks have been fired and so all the data retrieved.

Categories

Resources