I have a basic ReactiveCommand. No async wizardry, just plain old ReactiveCommand.Create(). I Subscribe() with the overload that takes an exception handler, but never hit the breakpoint in said exception handler (I did not expect this). I subscribe to ThrownErrors, never hit the breakpoint in that exception handler either (I sort of expected this).
Here's the example code:
var myCommand = ReactiveCommand.Create();
// this does not seem to work
myCommand.Subscribe(
_ => { throw new Exception("oops"); },
ex => {
Console.WriteLine(ex.Mesage);
Debugger.Break();
});
//this does not seem to work either
myCommand.ThrownExceptions.Subscribe(
ex => {
Console.WriteLine(ex.Mesage);
Debugger.Break();
});
I did my homework and checked the questions and answers in the topic.
ReactiveUI exception handling
How to catch exception from ReactiveCommand?
I have checked the mail list as well, and found this:
https://groups.google.com/forum/#!topic/reactivexaml/Dkc-cSesKPY
So I decided to change this to some async solution:
var myCommand = ReactiveCommand.CreateAsyncObservable(_ => this.Throw());
myCommand.Subscribe(
_ => { Console.WriteLine("How did we get here?"); },
// this is not expected to work
ex => {
Console.WriteLine(ex.Message);
Debugger.Break();
});
// however, I sort of expect this to work
myCommand.ThrownExceptions.Subscribe(
ex => {
Console.WriteLine(ex.Message);
Debugger.Break();
});
[...]
private IObservable<object> Throw()
{
Debugger.Break();
throw new Exception("oops");
}
And yet, I never hit any of my breakpoints, except the one in the Throw() method. :(
What am I doing wrong? How am I supposed to catch exceptions here?
Edit:
I do, however, hit the exception handler breakpoint when I throw the exception from within the observable, like this
private IObservable<object> Throw()
{
Debugger.Break();
return Task.Factory.StartNew(() =>
{
throw new Exception("oops");
return new object();
}).ToObservable();
}
Question modified to: "am I capable of handling an exception from within the method and not the observable?"
This is a bug in the current release of ReactiveUI - the exception thrown while creating the 'execute' observable is swallowed and the internal state of the ReactiveCommand is left in a broken state. This has been fixed in the next release.
See this github issue for details.
Related
I'm using NSubstitute for mocking and faking. I'm working with EF6 and would like to setup the SaveChangesAsync-Method of the database context to throw an exception:
context.SaveChangesAsync().Throws(new DbUpdateException("", SqlExceptionHelper.CreateSqlException(2627)));
SaveChangesAsync is called within a method of my data repository like this:
try
{
var fromDatabase = await context.Entries.OfType<Document>().FirstOrDefaultAsync(d => d.Id == doc.Id);
if (fromDatabase == null)
{
fromDatabase = new Document();
context.Entries.Add(fromDatabase);
}
PatchEntity(fromDatabase, doc);
await context.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
var innerException = ex.InnerException as SqlException;
if (innerException != null && innerException.Number == 2627)
{
errors.Add(new DbValidationError(nameof(doc.Name), "A entry with the same Name already exists under the selected parent."));
}
}
And this is the line within my unit test:
var result = await repository.TryAddOrUpdateDocument(doc);
Unfortunately my test keeps failing with the reason, that my test method(!) is throwing the exception, I'm trying to catch. Adding a general exception catch block isn't working either, the exception is not being catched at all. The exception is bubbling up.
My test is declared as "public async Task...", but turning it into simply void and calling .Result on the async method of my repository doesn't help either. What is going on?
I think the problem is that the exception is thrown from the original call instead of from inside the returned Task as described here.
Try something like:
Func<int> throwDbEx = () => {
throw new DbUpdateException("", SqlExceptionHelper.CreateSqlException(2627));
};
context.SaveChangesAsync().Returns(Task.Run(throwDbEx));
I cannot be 100% sure, but you are probably facing the problem that exceptions thrown by async with void return type cannot be caught naturally. Read the section "Avoid Async Void" here:
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
Even if it won't answer your problem, it is worth reading anyway...
Before I use Nito.MVVM, I used plain async/await and it was throwing me an aggregate exception and I could read into it and know what I have. But since Nito, my exceptions are ignored and the program jumps from async code block and continue executes. I know that it catch exceptions because when I put a breakpoint on catch(Exception ex) line it breaks here but with ex = null. I know that NotifyTask has properties to check if an exception was thrown but where I put it, it checks when Task is uncompleted, not when I need it.
View model:
public FileExplorerPageViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
_manager = new FileExplorerManager();
Files = NotifyTask.Create(GetFilesAsync("UniorDev", "GitRemote/GitRemote"));
}
Private method:
private async Task<ObservableCollection<FileExplorerModel>> GetFilesAsync(string login, string reposName)
{
return new ObservableCollection<FileExplorerModel>(await _manager.GetFilesAsync(login, reposName));
}
Manager method(where exception throws):
public async Task<List<FileExplorerModel>> GetFilesAsync(string login, string reposName)
{
//try
//{
var gitHubFiles = await GetGitHubFilesAsync(login, reposName);
var gitRemoteFiles = new List<FileExplorerModel>();
foreach ( var file in gitHubFiles )
{
if ( file.Type == ContentType.Symlink || file.Type == ContentType.Submodule ) continue;
var model = new FileExplorerModel
{
Name = file.Name,
FileType = file.Type.ToString()
};
if ( model.IsFolder )
{
var nextFiles = await GetGitHubFilesAsync(login, reposName);
var count = nextFiles.Count;
}
model.FileSize = file.Size.ToString();
gitRemoteFiles.Add(model);
}
return gitRemoteFiles;
//}
//catch ( WebException ex )
//{
// throw new Exception("Something wrong with internet connection, try to On Internet " + ex.Message);
//}
//catch ( Exception ex )
//{
// throw new Exception("Getting ExplorerFiles from github failed! " + ex.Message);
//}
}
With try/catch or without it has the same effect. This behavior is anywhere where I have NotifyTask.
Update
There is no event, that fires when exception occurred, but there is Property Changed event, so I used it and added this code:
private void FilesOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
throw new Exception("EXCEPTION");
bool failed;
if ( Files.IsFaulted )
failed = true;
}
And exception not fires.
I added throw exception in App class (main class) and it fired. And when I have exceptions that come from XAML, it also fires. So maybe it not fires when it comes from a view model, or something else. I have no idea. Will be very happy for some help with it.
Update
We deal with exception = null, but the question is still alive. What I wanna add, that I rarely this issue, when the app is starting to launch on the physic device. I read some info about it, and it doesn't seem to be related, but maybe it is:
I'm not entirely sure what your desired behavior is, but here's some information I hope you find useful.
NotifyTask is a data-bindable wrapper around Task. That's really all it does. So, if its Task faults with an exception, then it will update its own data-bindable properties regarding that exception. NotifyTask is intended for use when you want the UI to respond to a task completing, e.g., show a spinner while the task is in progress, an error message if the task faults, and data if the task completes successfully.
If you want your application to respond to the task faulting (with code, not just a UI update), then you should use try/catch like you have commented out in GetFilesAsync. NotifyTask doesn't change how those exceptions work; they should work just fine.
I know that it catch exceptions because when I put a breakpoint on catch(Exception ex) line it breaks here but with ex = null.
That's not possible. I suggest you try it again.
I know that NotifyTask has properties to check if an exception was thrown but where I put it, it checks when Task is uncompleted, not when I need it.
If you really want to (asynchronously) wait for the task to complete and then check for exceptions, then you can do so like this:
await Files.TaskCompleted;
var ex = Files.InnerException;
Or, if you just want to re-raise the exception:
await Files.Task;
Though I must say this usage is extremely unusual. The much more proper thing to do is to have a try/catch within your GetFilesAsync.
For some reason when an OperationCanceledException gets thrown inside an IDataflowBlock, the block does not propagate this exception to its IDataflowBlock.Completion task. Running the code sample below returns an unexpected IDataflowBlock.Completion.Status == TaskStatus.RanToCompletion.
However, if the thrown exception type in the block is changed to an ArgumentNullException, the IDataflowBlock.Completion.Status changes to TaskStatus.Faulted and the exception is saved in its InnerException property.
Any ideas why OperationCanceledException is getting swallowed?
[TestFixture]
public class TplDataBlockExceptionTest
{
[Test]
public void ShouldThrowException()
{
// Arrange
var block = new TransformBlock<int, string>(i =>
{
throw new OperationCanceledException();
return i.ToString();
});
// Act
block.Post(1);
block.Complete();
try
{
block.Completion.Wait();
}
catch (Exception)
{
// ignored
}
// Assert
Assert.That(block.Completion.IsFaulted);
}
}
I was able to reach Stephen Toub at Microsoft and he was able to confirm that this behavior is by design:
https://github.com/dotnet/corefx/blob/master/src/System.Threading.Tasks.Dataflow/src/Blocks/TransformBlock.cs#L186-L190
https://github.com/dotnet/corefx/blob/master/src/System.Threading.Tasks.Dataflow/src/Internal/Common.cs#L152-L175
ReSharper was giving me a CoVariantConversion warning so I decided to google this and see how to fix it. I came accross this snippet of code:
// ReSharper disable CoVariantArrayConversion
try
{
Task.WaitAll(taskList.ToArray());
}
catch (AggregateException ex)
{
ex.Handle(e => true);
}
// ReSharper restore CoVariantArrayConversion
This part is confusing me:
ex.Handle(e => true);
What does it do? I would think that it does nothing.
You are correct: the line can be removed and have the same effect (causing all the exceptions to be considered "handled") as if the line was there.
The only time it would be useful is if the lambda could return false for some exceptions (which it doesn't in this case).
This say, that the Exception is handled, nothing else.
Here is a sample that Shows how the Handle method could be used:
Task task = Task.Factory.StartNew(() =>
{
throw new UnauthorizedAccessException();
});
try
{
task.Wait();
}
catch (AggregateException ex)
{
ex.Handle(x =>
{
if (x is UnauthorizedAccessException)
{
// Handle this exception...
return true;
}
// Other exceptions will not be handled here.
return false;
});
}
The sample comes from this article: Asynchronous Programming - Exception Handling
I am trying to process some tasks asynchronously using Rx, e.g.
var list = Enumerable.Range(0, 100)
.ToObservable()
.SelectMany(x => Observable.Start(() => {
Console.WriteLine("Processing {0} ...", x);
Thread.Sleep(100 * x % 3);
if (x > 90) {
Console.WriteLine("Procesing exception {0} > 90", x);
throw new Exception("Value too large");
}
Console.WriteLine("Processing {0} completed.", x);
return x;
}))
.Subscribe(
x => { Console.WriteLine("Next [{0}]", x); },
e => {
Console.WriteLine("Exception:");
Console.WriteLine(e.Message);
},
() => { Console.WriteLine("Complete"); }
);
The problem I have with this code is that the exception is not passed to the subscriber. So, after a lot of trying I gave up and decided to ask this simple question:
How do you handle the exceptions raised from within asynchronous methods within a SelectMany statement?
Just to make it clear, the final implementation is a synchroneous function call that may or may not throw an exception. The goal is to pass it on to the subscriber so that it can be further processed (in the specific case a message will be shown to the user).
Edit
I moved my findings down to an answer, so that I can mark this question as answered. Personally, I do not agree with self answering ... but sometimes there is no other way, so sorry for it.
Use Materialize to convert your OnError / OnCompleted messages into notifications.
For example,
observable.SelectMany(x => Observable.Start(fn).Materialize())
will get you the error / completion wrapped in a notification to be handled in your actual subscription point way down the line, as opposed to the error being terminated inside the SelectMany.
This is useful for most Async call operations because the method either fails or completes.
The answer
Actually the code is working correctly. However, the debugger breaks at the exceptions as the async operations are still executed in the background - well at least those that were already started when the first exception occurred. Threw me! If you run the code without debugger the exceptions are swallowed.So I guess the problem was really in front of the computer :-)
Still some clarifications on the Observable.Start as I assumed - an this correctly - that the implemtation should have actually some error handling implemented ... see Background.
Background
Observable.Start is a convenience method that uses the Observable.ToAsync method to turn a function/acion into an async operation. If you look at the implementation of the method you'll see that it already does the exception handling/forwarding.
public static Func<IObservable<TResult>> ToAsync<TResult>(this Func<TResult> function, IScheduler scheduler) {
if (function != null) {
if (scheduler != null) {
return () => {
AsyncSubject<TResult> asyncSubject = new AsyncSubject<TResult>();
scheduler.Schedule(() => {
TResult result = default(TResult);
try {
result = function();
} catch (Exception exception1) {
Exception exception = exception1;
asyncSubject.OnError(exception);
return;
}
asyncSubject.OnNext(result);
asyncSubject.OnCompleted();
});
return asyncSubject.AsObservable<TResult>();
};
} else {
throw new ArgumentNullException("scheduler");
}
} else {
throw new ArgumentNullException("function");
}
}