Application dispatcher invoke freezes the application - wpf

I am having problem with the application freezing. Let me explain my scenario, I have a service which does an async call to a database to get a list of items, It is run by a task. Inside this task I have a try catch block, so it looks like this
public Task<List<T>> ComboListAsync(int? id = null, EnumDTO dto = EnumDTO.Default)
{
return Task.Run(() =>
{
using (var context = new ContextService())
{
try
{
return GetComboList(id, dto, context);
}
catch (Exception e)
{
Handler.DatabaseConnectionException();
throw;
}
}
});
}
Then it throws an exception as GetComboList its just this (for the moment)
protected virtual List<T> GetComboList(int? id, EnumDTO dto, ContextService context)
{
throw new NotImplementedException();
}
So the call catches the exception and goes inside here
public void Show(string message)
{
Message = message;
Application.Current.Dispatcher.Invoke(() =>
{
dialogView = new DialogView() {DataContext = this, Owner = Application.Current.MainWindow};
dialogView.ShowDialog();
});
}
Now the Dispatcher freezes the app, I tried to change it to use begin invoke, it does the same. Without the dispatcher I get an error message that the calling thread is not a STA. I simply want to display my message in a dialog window, that there was a problem connecting to a database. Can anyone help?
I looked online and there is many threads about dispatcher, but none actually show a solution that will fix my issue.
Thank you
EDIT
Code which calls the ComboListAsync
protected override void RetrieveRelatedActiveLists()
{
MyCollection = service.ComboListAsync().Result;
}

Its a deadlock because of the calling code is using the .Result.
Using service.ComboListAsync().Result makes the UI thread await for this method to return, when you call Application.Current.Dispatcher.Invoke from within it you are sending a message to the UI thread that is awaiting the return of method itself.
You must await the method service.ComboListAsync() like this:
protected override async void RetrieveRelatedActiveLists()
{
MyCollection = await service.ComboListAsync();
}

Related

async Task Main() in WinForms application having async initialization

We're having a winforms application that uses an async initialization process. Simplified you can say that the application will run the following steps:
Init - this runs async
Show MainForm
Application.Run()
The currently existing and working code looks like this:
[STAThread]
private static void Main()
{
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
var task = StartUp();
HandleException(task);
Application.Run();
}
private static async Task StartUp()
{
await InitAsync();
var frm = new Form();
frm.Closed += (_, __) => Application.ExitThread();
frm.Show();
}
private static async Task InitAsync()
{
// the real content doesn't matter
await Task.Delay(1000);
}
private static async void HandleException(Task task)
{
try
{
await Task.Yield();
await task;
}
catch (Exception e)
{
Console.WriteLine(e);
Application.ExitThread();
}
}
The background how this is working is described very detailed by Mark Sowul here.
Since C# 7.1 we're able to use async Task in main method. We tried it in a straight forward way:
[STAThread]
private static async Task Main()
{
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
try
{
await StartUp();
Application.Run();
}
catch (Exception e)
{
Console.WriteLine(e);
Application.ExitThread();
}
}
private static async Task StartUp()
{
await InitAsync();
var frm = new Form();
frm.Closed += (_, __) => Application.ExitThread();
frm.Show();
}
private static async Task InitAsync()
{
// the real content doesn't matter
await Task.Delay(1000);
}
But that doesn't work. The reason is clear. All the code after the first await will be forwarded to the message loop. But the message loop hasn't startet yet because the code that starts it (Application.Run()) is located after the first await.
Removing the synchronization context will fix the problem but causes to run the code after await in a different thread.
Reordering the code to call Application.Run() before the first await will not work because it is a blocking call.
We try to use the new feature of having an async Task Main() that allows us to remove the HandleException-solution that is hard to understand. But we don't know how.
Do you have any suggestions?
You don't need async Main. Here is how it can possibly be done:
[STAThread]
static void Main()
{
void threadExceptionHandler(object s, System.Threading.ThreadExceptionEventArgs e)
{
Console.WriteLine(e);
Application.ExitThread();
}
async void startupHandler(object s, EventArgs e)
{
// WindowsFormsSynchronizationContext is already set here
Application.Idle -= startupHandler;
try
{
await StartUp();
}
catch (Exception)
{
// handle if desired, otherwise threadExceptionHandler will handle it
throw;
}
};
Application.ThreadException += threadExceptionHandler;
Application.Idle += startupHandler;
try
{
Application.Run();
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
Application.Idle -= startupHandler;
Application.ThreadException -= threadExceptionHandler;
}
}
Note, if you don't register threadExceptionHandler event handler and StartUp throws (or anything else on the message loop throws, for the matter), it will still work. The exception will be caught inside the try/catch which wraps Application.Run. It will just be a TargetInvocationException exception with the original exception available via its InnerException property.
Updated to address the comments:
But for me it looks very strange to register an EventHandler to the
idle event so startup the whole application. It's totally clear how
that works but still strange. In that case I prefer the
HandleException solution that I already have.
I guess it's a matter of taste. I don't know why WinForms API designers didn't provide something like WPF's Application.Startup. However, in the lack of a dedicated event for this on WinForm's Application class, deferring specific initialization code upon the first Idle event is IMO an elegant solution, and it's widely used here on SO.
I particularly don't like the explicit manual provisioning of WindowsFormsSynchronizationContext before Application.Run has started, but if you want an alternative solution, here you go:
[STAThread]
static void Main()
{
async void startupHandler(object s)
{
try
{
await StartUp();
}
catch (Exception ex)
{
// handle here if desired,
// otherwise it be asynchronously propogated to
// the try/catch wrapping Application.Run
throw;
}
};
// don't dispatch exceptions to Application.ThreadException
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
using (var ctx = new WindowsFormsSynchronizationContext())
{
System.Threading.SynchronizationContext.SetSynchronizationContext(ctx);
try
{
ctx.Post(startupHandler, null);
Application.Run();
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
System.Threading.SynchronizationContext.SetSynchronizationContext(null);
}
}
}
IMO, either approach is more clean than the one used in your question. On a side note, you should be using ApplicationContext to handle the form closure. You can pass an instance of ApplicationContext to Application.Run.
The only point that I'm
missing is your hint that the synchronization context is already set.
Yes it is - but why?
It is indeed set as a part of Application.Run, if not already present on the current thread. If you like to learn more details, you could investigate it in .NET Reference Source.

InvokeCommand from Observable.Timer causes cross-thread issue

I have a ReactiveCommand that refreshes data and is bound to a Button in XAML. The functionality works fine, but I also want to execute the command on a timer.
I have the following code - SetupAutoRefresh is called from the ctor in my VM, but when the Observable fires, I get an exception with the message: "The calling thread cannot access this object because a different thread owns it."
VM:
private void SetupAutoRefresh() {
Observable.Timer(TimeSpan.FromSeconds(5))
.Select(_ => Unit.Default)
.ObserveOn(RxApp.MainThreadScheduler)
.InvokeCommand(RefreshData);
RefreshData = ReactiveCommand.CreateFromTask(Refresh);
}
private async Task Refresh()
{
var updatedData = await _repository.GetAll();
Data.Merge(updatedData);
}
private ReactiveCommand<Unit, Unit> _refreshData;
public ReactiveCommand<Unit, Unit> RefreshData
{
get { return _refreshData; }
set { this.RaiseAndSetIfChanged(ref _refreshData, value); }
}
private IReactiveList<Model> _data;
public IReactiveList<Model> Data
{
get { return _data; }
set { this.RaiseAndSetIfChanged(ref _data, value); }
}
XAML:
<Button Grid.Column="2"
Command="{Binding RefreshData}"
Style="{StaticResource ToolbarButtonTheme}"
Content="{StaticResource RefreshToolbarIcon}"
ToolTip="Refresh Search"/>
Debug output provides this stacktrace:
at System.Windows.Threading.Dispatcher.VerifyAccess()
at System.Windows.DependencyObject.GetValue(DependencyProperty dp)
at System.Windows.Controls.Primitives.ButtonBase.get_Command()
at System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute()
at System.Windows.Controls.Primitives.ButtonBase.OnCanExecuteChanged(Object >sender, EventArgs e)
at
System.Windows.Input.CanExecuteChangedEventManager.HandlerSink.OnCanExecuteChanged(Object sender, EventArgs e)
at ReactiveUI.ReactiveCommand.OnCanExecuteChanged() in C:\projects\reactiveui\src\ReactiveUI\ReactiveCommand.cs:line 628
I've tried many different variations of attempting to schedule this on the RxApp.MainThreadScheduler but without any joy - ObserveOn, SubscribeOn, setting the output scheduler... none of which I had much hope for anyway.
Feel like I'm missing something obvious here, but have been banging my head against a brick wall for the whole afternoon. Surely this scenario is possible in RxUI?
The Refresh method runs on a background thread; you can't modify databound properties within that method.
Try this:
private void SetupAutoRefresh() {
Observable.Timer(TimeSpan.FromSeconds(5))
.Select(_ => Unit.Default)
// remove ObserveOn here; the Command will run on the background
.InvokeCommand(RefreshData);
RefreshData = ReactiveCommand.CreateFromTask(Refresh);
// RefreshData.Subscribe is guaranteed to run on the UI thread
RefreshData.Subscribe(listOfModels => Data.Merge(listOfModels))
}
private async Task Refresh()
{
// all this method does is deliver a list of models
return await _repository.GetAll();
}
// return IEnumerable<Model> from the command
public ReactiveCommand<Unit, IEnumerable<Model>> RefreshData
Now, your ReactiveCommand simply fetches the new data, and returns it to you on the UI thread within Subscribe :)
Figured out the issue - looks like the Observable needed to be created on the UI thread. I missed it from the original post, but the SetupAutoRefresh method had been called from another async method, which had switched context during a prior await.

Global Error Handling in Task.Run for realtime User ntofication

I have a wpf c# app.
I normally use a global error handler to catch all errors:
private void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
try
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => Xceed.Wpf.Toolkit.MessageBox.Show(e.Exception.ToString(), "Error",
MessageBoxButton.OK, MessageBoxImage.Error)));
e.Handled = true;
InformedWorkerDataService.Common.Shared.RecordMessage(e.Exception.ToString(), true);
}
finally { }
}
However, if start off a task.run 'bit of code' and it throws an error then i have observed that error is not caught:
Task.Run(() =>
{
throw and error here
});
so I have to put a 'Try-Catch' thing in to capture it:
Task.Run(() =>
{
try
{
throw an error here
}
catch (Exception ex)
{
do something with error
}
});
~ which defeats the object of having a Global Error handler
But, if i use this approach:
TaskScheduler.UnobservedTaskException += (s, e) => {
e.Exception //The Exception that went unobserved.
e.SetObserved(); //Marks the Exception as "observed," thus preventing it from triggering exception escalation policy which, by default, terminates the process.
};
... it will do my global exception handling but if I want to notify the user of the error real-time it does not do so very well because IT IS on a separate thread.
What would be a good compromise?
Unfortunately the TaskScheduler.UnobservedTaskException is not guarenteed to fire in real time with the exception being thrown. This means that using this handler for user notification can be quite confusing as the user action and error notification will not happen synchronously. For user driven handling of 'unexpected' task exceptions, you could create helper methods as below and use TaskEx.Run instead of Task.Run:
public static class TaskEx
{
public static Task Run(Action function)
{
return Task.Run(() =>
{
try
{
function();
}
catch (Exception ex)
{
TraceEx.TraceException(ex);
//Dispatch your MessageBox etc.
}
});
}
}
Obviously this is not as simple as adding a global handler (which should still be done for tracing purposes) but is simple enough to implement in UI driven code.

Ria Service And OOB : check if service is reachable, MEF not importing my views

Hi currently I am trying to check if the Ria Service is available for our OOB application.
public static void IsServiceReachable(Action onServiceAvailable, Action onServiceUnavailable)
{
try {
DomainContext context = new DomainContext();
InvokeOperation<bool> invokeOperation = context.IsAlive();
invokeOperation.Completed += (s, arg) => onServiceAvailable();
}
catch (Exception) {
onServiceUnavailable();
}
}
When the exception happen my App hangs, and is now just a white screen. Am I doing it correctly?
I am also using MEF in the app, I am lazy importing my views, sadly when Ria Service is not reachable, MEF doesnt import my views :( I am calling CompositionInitializer.SatisfyImports(this).
[ImportMany(AllowRecomposition = true)]
public Lazy<BaseUserControl, IViewMetadata>[] Views { get; set; }
Have you tried checking if an error has occured in the OnServiceAvailable callback:
void OnServiceAvailable(object sender, EventArgs e)
{
InvokeOperation op = sender as InvokeOperation;
if (op.HasError) {
Exception exception = op.Error;
...
} else {
...
}
}
You should probably rename OnServiceAvailable something like OnOperationComplete.
You have to handle the errors in the callback - including the 'ServiceNotAvailable' error. Remember this is an asyncronous call - the client does does not wait for the server response before it continues.

Windows phone 7 How to create events to update UI

Hi I'm making an app that when it launches makes a HttpWebRequest, receives some XML and puts it in a list. This code is in the Application_Launching method in App.xaml.cs . This list is then used in a listpicker on the first page of the app.
However because HttpWebRequest executes on a different thread the list is not populated when I assign it to to the Listpickers itemSource.
I've been told I should have an event that fires after the list is full and a listener on my first page to populate the list when this happens. How would I declare this event and its listener?
You can use HttpWebRequest and make AsyncCallback or use WebClient class which has an event DownloadStringCompleted. An example.
public void GetXMLfromServer()
{
try
{
string url = "";//your url here
HttpWebRequest request =
(HttpWebRequest)HttpWebRequest.Create(new Uri(url));
request.BeginGetResponse(new AsyncCallback(GetXMLfromServerCompleted),
request);
}
catch (Exception ex)
{
}
}
private void GetXMLfromServerCompleted(IAsyncResult asynchronousResult)
{
try
{
string resultString = "";
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
using (StreamReader streamReader1 = new StreamReader(response.GetResponseStream()))
{
resultString = streamReader1.ReadToEnd();
}
//**Put your code here to populate the list**
}
catch (Exception ex)
{
}
}

Resources