Threading in Winforms, generate HTML report on completion of tasks - winforms

I have a Winforms application that schedules some work using a service. The service has a callback that updates the database with the work proceedings.
Now let's say I schedule x work items. After the x work items are all completed, I want to generate an HTML report about the work statistics. I think the only way I can check the work completion of all items is to see their completion statuses in the database.
Can someone tell me how and when I can generate the HTML report? I think I can use a thread to poll the db to see if all work completed and inform UI to generate the report, but don't know how to implement that.

You can use BackgroundWorker component for waiting async task completed. It's easy.
First - drag BackgroundWorker from Toolbox to your form.
Second - when you start processing (e.g. on button click event) add following code:
private void button_Click(object sender, EventArgs e)
{
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
backgroundWorker1.RunWorkerAsync();
}
Next - add to DoWork event handler code that starts processing and polling database:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// schedule some work
// poll database
}
And last - add RunWorkerCompleted handler, which will run just after DoWork completed (i.e. all tasks updated their state in database):
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// generate report
}
Thats it. Btw BackgroundWorker could report progress during polling database. Little searching will help you how to do that :)

You can use Task.ContinueWith and Task.Wait
Example: Using Task
Task.ContinueWith:
This extends the initial task. Run immediatly after it finishes
Task.Wait
You would use this if you are running several task that provide the same result be may do so at different times, i.e. fetching same data from different web services.(Just an example).
This will execute as soon as one of the appropriate task successfully finishes.

Related

Wait until a background worker is completed without locking out the main thread

I'm trying to have a background worker running with a spinner running in the main thread and I would like to wait until the background worker is finished. Currently I can have one or the other. I have tried using a Auto Reset Event but that locks out the main thread therefore not displaying the spinner. Is there something similar to Auto Reset Event that does not lock out the main UI.
Here is some of my code
BackgroundWorker _bWorker = new BackgroundWorker();
_bWorker.DoWork += _bWorker_DoWork ;
_bWorker.RunWorkerCompleted += _bWorker_RunWorkerCompleted;
AutoResetEvent are = new AutoResetEvent(false);
_bWorker.RunWorkerAsync();
// Wait here until the background worker is finished
are.WaitOne();
...
private void _bWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Show spinner
WaitSpinnerAdornerText = "Working";
IsWaitSpinnerVisible = true;
...
are.Set();
}
private void _bWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
IsWaitSpinnerVisible = false;
...
}
Any ideas? Any help is welcome! Thanks in advance.
The whole point of using a BackgroundWorker is that you don't 'wait' for it to complete. Just remove the AutoResetEvent and trigger any logic that currently resides after the RunWorkerAsync in the _RunWorkerCompleted.
Alternatively you could look into using the Task Parallel Library and have that code run as a continuation.
Lastly you could look into using the Async CTP for .net 4.0 in which the async/await features behave nearly identically to how you want to write the code.

Has any one out there in the field used Dispatcher.Yield?

In "Lucian Wischik - Async Part 2 -- deep dive into the new language feature of VB/C#" for NDC 2012, the recommended use of Dispatcher.Yield is introduced to me. Does anyone out there have examples (and explanations) of how (and why) this call is used in the wild?
Well for example if you have a long running task but you still need to update your UI you can use Yield.
Yield gives you the ability to leave the current thread context and allow other code to run in the underlying context.
public async void MyButton_Click(object sender, RoutedEventArgs e)
{
for( int i=0; i < 10000; i++)
{
ProcessSomeStuff(i);
// await the Yield to ensure all waiting messages
// are processed before continuing
await Task.Yield();
}
}
In the example above you can process stuff async but calling Yield will allow events on the UI thread to execute also,
I love to use "await Dispatcher.Yield()" whenever I develop WPF applications.
It is easy to use and barely makes any problems in most cases.
The most useful case using Dispatcher.Yield() is when there is a small time of lag in a operation, which can not be bypassed by "Task", "Thread" etc.
For example, let's say that there is a command button which opens a new window or tab.
private void aButton_Click(object sender, RoutedEventArgs e)
{
// Do some ui stuff
// You can not use Task here at large.
}
What happens here is the whole application stopping, and the button UI remains pressed until the new window or tab opens. It is bad user experience and makes the apllicaiton seem so much slow.
and here is a code with a trick.
private async void aButton_Click(object sender, RoutedEventArgs e)
{
await Dispatcher.Yield();
// Do some ui stuff
}
In here, your code works differently. Dispathcer will process other ui operations first before getting into the job. The button pressed motion will be released first and then a new window will come.
Application stopping will be same, but to users the application will be much smooth and fast. So it is usually a good idea to adopt Dispatcher.Yield() in your application.
In addition, Task.Yield() is different from Dispatcher.Yield(). Try two options and see the results.

OutOfMemoryException while bulk data processing with WCF RIA & WF4

I have an existing Silverlight 5 application. I'm adding a page to it to allow users to process mass updates to data in a 3rd party database system. The application currently uses WCF RIA services to communicate to the 3rd party system via SOAP. The functionality of the update is contained in a Workflow 4 application I created and is referenced as an assembly on the server-side of the SL application. Lastly, the application is hosted right now in my local instance of IIS 7.5 running on Windows 7; I'm also debugging with IIS, not the VS dev server.
At the basic level, the application functions as follows:
Select text file
Click "Start" button
Event handler creates an instance of a user-defined Type that keeps track of the batch
Event handler creates a new BackgroundWorker instance and wires up handlers for the DoWork, ProgressChanged, and RunWorkerCompleted events
Event handler calls RunWorkerAsync()
Here's the shortened code for the DoWork event handler, since that's where the majority of the work is done.
private void BwOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs, BatchContainerControl batchProcess)
{
var worker = sender as BackgroundWorker;
// Iterate through each record of data file and call the 'UpdateAddress' function
// of the AddressDomainService which, in turn, executes the Workflow
foreach (var item in batchProcess.FileData)
{
// Check if operation has been cancelled
if (worker.CancellationPending)
{
doWorkEventArgs.Cancel = true;
break;
}
. . .
// Invoke THINKComm.CustomerAddressUpdate Workflow via AddressContext
var invokeOp = _addressDomainContext.UpdateAddress(activityData);
// 'activityData' is an instance of Dictionary<string, string>
invokeOp.Completed += (o, args) => InvokeOpOnCompleted(o, args, batchProcess);
}
}
The handlers for the ProgressChanged and RunWorkerCompleted events, as well as the Completed event of the InvokeOperation instance all, for the most part, update a part of the UI. If you think posting any of that code would be helpful, I'd be happy to update the post.
Speaking of UI, the parts that are updated by the event handlers are two ProgressBar controls - one that tracks the records as they're read from the file and a second one that tracks the records as the update has taken place on the 3rd party database.
Getting to the actual problem...
I've processed files of 10, 100, and 1,000 records with no problem. I then attempted to process a complete file containing ~15,000 records (or 1,907KB of data). The process starts and I can see in the debugger output that the Workflow is being executed. About a quarter of the way through or so, I get an OutOfMemoryException. Here's the stack trace:
at System.ServiceModel.DomainServices.Client.WebDomainClient`1.BeginInvokeCore(InvokeArgs invokeArgs, AsyncCallback callback, Object userState)
at System.ServiceModel.DomainServices.Client.DomainClient.BeginInvoke(InvokeArgs invokeArgs, AsyncCallback callback, Object userState)
at System.ServiceModel.DomainServices.Client.DomainContext.InvokeOperation(String operationName, Type returnType, IDictionary`2 parameters, Boolean hasSideEffects, Action`1 callback, Object userState)
at THINKImportSystem.Web.Address.AddressDomainContext.UpdateAddress(Dictionary`2 activityData)
at THINKImportSystem.BatchProcessPage.BwOnDoWork(Object sender, DoWorkEventArgs doWorkEventArgs, BatchContainerControl batchProcess)
at THINKImportSystem.BatchProcessPage.<>c__DisplayClass10.<StartButtonClick>b__6(Object s, DoWorkEventArgs args)
at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)
at System.ComponentModel.BackgroundWorker.OnRun(Object argument)
Then, the JIT debugger pops up with an error of Unhandled Error in Silverlight Application Code:4004 with a message of System.ServiceModel.DomainServices.Client.DomainOperationException: Invoke operation 'UpdateAddress' failed. Error HRESULT E_FAIL has been returned from a call to a COM component.
I should mention that, sometimes, I get the JIT debugger first. I see in the Debug output that threads are still exiting, and then about 10 or 20 seconds later, the VS debugger pops up with the out of memory exception.
My best guess is that, objects somewhere (maybe related to the DomainService?) aren't being released and therefore, memory usage is building. From what I understand, IIS places restrictions on the amount of memory an application can use, but I can't tell if that's the case here or not.
I was thinking that, each time a record in the file is processed, the objects related to it's processing would be released and therefore overall memory usage would be pretty low. But obviously I'm not understanding how everything is being executed!
I was also wondering if using the TPL as opposed to BackgroundWorker would make a difference?

MessageBox.Show early in App startup causes app to terminate

As part of my App's startup procedure, it checks data integrity, and if it finds a problem it pops up a message to the user telling them that it might take a while to repair things.
I'm showing the message using MessageBox.Show. Because the data check is done from a worker thread, I'm switching over to the UI thread to make that call, and then setting a ManualResetEvent to tell the worker thread when the user has acknowledged the message.
I kick off the data check/load very early in the app's lifecycle from the constructor in the main Application class, by spinning off a worker thread (using the ThreadPool).
When I run with the debugger, and the message is displayed, the app just waits for input. When I run without the debugger, the app terminates after displaying the dialog for 10 seconds.
That 10 seconds is a big clue - it tells me that the OS thinks the app took too long to initialize (the OS kills apps that take too long to start up).
I think that my MessageBox.Show is blocking the UI thread before the App.RootFrameNavigating has a chance to be invoked.
My questions:
Does my diagnosis sound right?
I'd prefer to kick off my data load early, because it is almost entirely IO, except for this Message Box, and the sooner I can get my Model loaded, the better, but do you normally delay your data load until later in the app lifecycle?
Any other ideas/suggestions? I can't guarantee which page will be the start page, because the app could be resuming to any page. I'm also thinking of having the MessageBox.Show delay itself until the app has initialized, perhaps polling away for a flag set by App.RootFrameNavigating - does that make sense?
I think your problem is a result of kicking off the worker thread in the Application constructor. You should use the appropriate life-cycle event, in this case: PhoneApplicationService.Activated Event
So, the solution I've come up with is to still kick off the data load in a worker-thread from the Application's constructor, but in my PhoneService's class ShowDialog method that I invoke to invoke MessageBox.Show, I check to see if the initial navigation has occurred:
private readonly ManualResetEvent _appInitialized = new ManualResetEvent(false);
public void AppInitialized()
{
_appInitialized.Set();
}
public void ShowDialog(string caption, string text, Action<MessageBoxResult> callback, MessageBoxButton button = MessageBoxButton.OKCancel)
{
_appInitialized.WaitOne();
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
var result = MessageBox.Show(text, caption, button);
if (callback != null)
{
callback(result);
}
});
}
Then in my Application class:
private bool _firstNavigate = true;
private void RootFrameNavigating(object sender, NavigatingCancelEventArgs e)
{
if (_firstNavigate)
{
_firstNavigate = false;
var navigationService = (NavigationService) sender;
navigationService.Navigated += NavigationServiceNavigated;
}
....
private void NavigationServiceNavigated(object sender, NavigationEventArgs e)
{
var navigationService = (NavigationService)sender;
navigationService.Navigated -= NavigationServiceNavigated;
PhoneServices.Current.AppInitialized();
}
Anyone see any issues with this approach? Anyone come up with a better way?

Complex multi-threaded interface

First of all its not a splash-screen what i want... just to be clear... ok... lets go to the description of the problem:
i have a form that fire N number of threads (i dont know how many, the user must choose)... each thread has a object, and during several moments the objects may fire a event to signal some change... there must be a form for each thread to "report" the messages that the events are sending...
my problem is: the threads create the forms perfectally... but the desappear... out of nowhere... they appear on the screen... and vanish... poof.... gone! how can i avoid that undesired "disposing"?!?!
Your threads must either
use proper InvokeRequired + Invoke logic
or run their own MessagePump (Application.Run)
Which one did you (not) do?
If you create a form in a thread, the form will vanish when the thread is done. If you want the form to survive longer than that you need to either keep the thread alive, or create the form on the application's main thread. The latter would be preferable. Just make sure that each to hook up event listener for the object in the corresponding form, and use Invoke or BeginInvoke as needed when updating the form.
A simple example:
First a worker:
class Worker
{
public event EventHandler SomethingHappened;
protected void OnSomethingHappened(EventArgs e)
{
var evnt = SomethingHappened;
if (evnt != null)
{
evnt(this, e);
}
}
public void Work()
{
// do lots of work, occasionally calling
// OnSomethingHappened
}
}
Then, in a form we have an event handler for the SomethingHappened event:
public void SomethingHappenedHandler(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new Action(() => SomethingHappenedHandler(sender, e)));
return;
}
// update gui here
}
Then it's really just a matter of wiring it all together:
Worker w = new Worker();
ProgressForm f = new ProgressForm;
w.SomethingHappened += f.SomethingHappenedHandler;
f.Show();
Thread t = new Thread(w.Work);
t.Start();
Disclaimer: this sample is quickly tossed together and somewhat untested (sitting on the train, about to get off ;) ).
A Form must be hosted on a thread with a message loop. You can create a message loop by either calling Application.Run or Form.ShowDialog. However, unless you have really good reason for doing so I would avoid having more than one thread with a windows message loop.
I would also avoid creating N threads. There are better ways to parallelize N operations other than creating one thread per operation. To name only two: 1) queue a work item in the ThreadPool or 2) use the Task Parallel Library via the Task class. The problem with creating N threads is that each thread consumes a certain amount of resources. More threads means more resources will be consumed and more context switching will occur. More is not always better in the world of multithreading.

Resources