I am developing an ASP.NET Core 3.1 API for my React front end app.
My problem is that my exceptions are not trickling up through my object hierarchy as I expect. I think it might be due to some multi-threading issue, but I don't know enough about C# to be sure! I am learning on Pluralsight, but I'm currently studying networking, which is not going to help me with this!
The calling code is a SignalR Hub method, that looks like this:
public async Task<bool> UpdateProfile(object profileDto)
{
try
{
ProfileDTO profile = ((JsonElement) profileDto).ToObject<ProfileDTO>();
_profile.UpdateProfile(profile);
return true;
}
catch (Exception e)
{
return false;
}
}
I would expect that any exceptions thrown or not handled in _profile.UpdateProfile(profile); would cause the exception block here to return false. My UpdateProfile() looks like this:
public void UpdateProfile(ProfileDTO profileDto)
{
_databaseService.ExecuteInTransaction(async session =>
{
// simulate an error
throw new Exception("Some exception");
});
}
...and my ExecuteInTransaction() looks like this:
public async void ExecuteInTransaction(Func<IClientSessionHandle, Task> databaseAction)
{
using var session = await Client.StartSessionAsync();
try
{
session.StartTransaction();
await databaseAction(session);
await session.CommitTransactionAsync();
}
catch(Exception e)
{
await session.AbortTransactionAsync();
throw e;
}
}
I would expect that the exception thrown in UpdateProfile() would trickle up to the catch block in ExecuteInTransaction() — which it does — but then further, I would expect this exception to trickle up to the Hub UpdateProfile() method. Instead, it ends up in the Throw() method on the ExceptionDispatchInfo class in the System.Runtime.ExceptionServices namespace.
Reading through the comments in this file makes me think it is a threading issue, but I don't know enough about how threading works yet in C#. Is it possible for the exceptions thrown in UpdateProfile() to make it up to the top level of my Hub UpdateProfile()? (just noticed they confusingly have the same name).
Your problem is the async void signature of ExecuteInTransaction.
Async void methods have different error-handling semantics. When an exception is thrown out of an async Task or async Task method, that exception is captured and placed on the Task object. With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started
Source
What this means, if you're using ASP.NET Core where there is no SynchronizationContext, your exception will be thrown most likely on a threadpool thread if you didn't mess around with the TaskScheduler. If you're on older .NET framework code, it will be on the captured context but either way, what you know about exception handling does not apply here. You can catch these exceptions by subscribing to AppDomain.UnhandledException but no one wants to do that in maintainable code.
To fix this, change public async void ExecuteInTransaction to public async Task ExecuteInTransaction, change public void UpdateProfile to public async Task UpdateProfile and call it like so:
public async Task<bool> UpdateProfile(object profileDto)
{
try
{
ProfileDTO profile = ((JsonElement) profileDto).ToObject<ProfileDTO>();
await _profile.UpdateProfile(profile);
return true;
}
catch (Exception e)
{
return false;
}
}
public async Task UpdateProfile(ProfileDTO profileDto)
{
await _databaseService.ExecuteInTransaction(async session =>
{
// simulate an error
throw new Exception("Some exception");
});
}
Related
I'm trying to call an async task (SIn) within a synch method (SignIn). I need the synch method because I'm passing ref to that method. But when I'm calling the async task, the GUI is frozen. The async task is a simple login with the onedrive sdk.
I've tried to waited the task, but the GUI still frozen. I've also tried creating a new Thread, but it didn't work too. How can I call the async method?
public override bool SignIn(ref User user)
{
try
{
signInEnd = false;
signinUser = user;
Task<bool> task = SIn();
task.Wait();
return task.Result;
}
catch(Exception e)
{
return false;
}
}
public async Task<bool> SIn()
{
var msaAuthProvider = new MsaAuthenticationProvider(
this.oneDriveClientId,
this.oneDriveReturnUrl,
this.scopes,
new CredentialVault(this.oneDriveClientId));
await msaAuthProvider.AuthenticateUserAsync();
driveClient = new OneDriveClient(this.oneDriveBaseUrl, msaAuthProvider);
}
Calling Wait() blocks the UI thread which means that the continuation of SIn(), i.e. the part that will eventually be executed once the Task returned by AuthenticateUserAsync() has completed, won't be able to execute on this thread. This results in a deadlock.
You may be able to get around this by avoiding capturing the context by calling ConfigureAwait(false) in SIn():
public async Task<bool> SIn()
{
var msaAuthProvider = new MsaAuthenticationProvider(
this.oneDriveClientId,
this.oneDriveReturnUrl,
this.scopes,
new CredentialVault(this.oneDriveClientId));
await msaAuthProvider.AuthenticateUserAsync().ConfigureAwait(false);
driveClient = new OneDriveClient(this.oneDriveBaseUrl, msaAuthProvider);
}
But the "real" solution to this kind of issues is not to mix asynchronous and synchronous code, i.e. SignIn should be asynchronous and await SIn(). Don't block on asynchronous code by calling Wait() or Result:
public Task<bool> SignIn(User user)
{
try
{
...
return await SIn();
}
catch (Exception e)
{
return false;
}
}
Please refer to #Stephen Cleary's blog post for more information about this.
mm8 is right that not calling async from inside a sync method is the best way to solve your issue,
remember that the public async void EventHandler() method was specifically designed for running long running tasks from a gui linked control
However it isn't always possible to rewrite an entire system to be async when only one small section needs changing
In this case you should avoid waiting for the results as this makes the async process pointless, what you can do though is break your synchronous code into 2 parts a before and after
the before method will prep and launch the task,
the after handles the results
ie
public async Task<string> GetData(int delay)
{
await Task.Delay(delay);
return "complete";
}
public void StartGettingData()
{
GetData(5000).ContinueWith(t => CompleteGetData(t.Result), TaskScheduler.Current);
}
public void CompleteGetData(string message)
{
UpdateStatus(message);
}
this method does have the added complexity of requiring you to ensure thread safety yourself, which is why the async/await functionality was introduced
Suppose my service is runing and i have used a code for sending email notification in that service.
The "EmailNotification" method is async and await.
Code of EmailNotification:
public async task EmailNotification()
{
try
{
//code here
using (SmtpClient smtp = new SmtpClient())
{
//mail sending
await smtp.Send(mailMessage);
}
}
Catch(Exception ex)
{
}
}
Used EmailNotification methos in my some test method like that:
public void test()
{
EmailNotification();
}
My Problem:
1)How can i log execptions of async and await method if my destination method test is not of type async?
2)Is it possible to use async method in non async type like above ia m using in test method?
How can i log execptions of async and await method if my destination method test is not of type async?
The task returned from the async method will contain any exceptions from that method. However, calling it in a "fire and forget" manner such as this means that the returned task is ignored. So, you'll have to have a try/catch in your async method (which is already there) and log in the catch.
Is it possible to use async method in non async type like above ia m using in test method?
Possible? Sure, it'll compile and run.
A good idea? Probably not.
On ASP.NET, any work done outside of an HTTP request is not guaranteed to complete. When your code calls EmailNotification, it is starting some work and then completing the HTTP request (by sending a response). That send-email work is then done without an HTTP request, and may be lost if your app is recycled.
This is a fine approach if you're perfectly OK with emails disappearing occasionally without any logs or any other indicators that something went wrong. If you're not OK with that, then you'll need a more robust solution (such as a proper distributed architecture, as I describe on my blog). Or you can just outsource that part of it by using an email service such as SendGrid.
public static class TaskExtensions
{
/// <summary>
/// Waits for the task to complete, unwrapping any exceptions.
/// </summary>
/// <param name="task">The task. May not be <c>null</c>.</param>
public static void WaitAndUnwrapException(this Task task)
{
task.GetAwaiter().GetResult();
}
/// <summary>
/// Waits for the task to complete, unwrapping any exceptions.
/// </summary>
/// <param name="task">The task. May not be <c>null</c>.</param>
public static T WaitAndUnwrapException<T>(this Task<T> task)
{
return task.GetAwaiter().GetResult();
}
}
And then use it like this:
try
{
var t = EmailNotification();
t.WaitAndUnwrapException();
}
catch(Exception ex)
{
// log...
}
Alternatively:
public void test()
{
try
{
var t = EmailNotification();
t.GetAwaiter().GetResult();
}
catch(Exception ex)
{
// Do your logging here
}
}
You should always try to use await/async all the way, and avoid this pattern when possible. But when you need to call an async method from a non-async method you can use GetAwaiter().GetResult() in order to await the task and also get the correct Exception (if any).
As mentioned in the comments there's already an excellent answer from Stephen Cleary in this question:
How to call asynchronous method from synchronous method in C#? (which my code is based on)
When I am using async await and an exception is thrown the thread context is being lost. In my code I'm using dependency injection that registered to resolve per thread so I need to execute my code on the same thread.
This is how it is setup:
I have a method that will try calling different communicators using async when one throws an exception it will go onto the next one:
public async Task<TResponse> VisitRequestAsync(Context context)
{
/* ....
prepare request from context
.... */
var communicatorEnumerableInstance = _communicatorService.GetCommunicatorInstanceEnumerable();
foreach (var communicator in communicatorEnumerableInstance)
{
using (communicator)
{
var communicatorInstance = communicator as ICommunicator<TResponse, TRequest>;
try
{
return await communicatorInstance.ProcessAsync(request).ConfigureAwait(true);
break;// call will break out of the for-each loop if successful processed.
}
catch (Exception exception)
{
continue;// Continue to load next communication method/instance
}
}
}
}
Below is a unit test that contains a communicator that always throws an exception and one that tries to get a dependency that is registered onto the original thread.
public class TestDependancy : ITestDependancy
{
}
public interface ITestDependancy
{ }
public class TestCommunicatorThrowsException :
ICommunicator<ResponseType, RequestType>
{
public async Task<ResponseType> ProcessAsync(RequestType request)
{
var task = Task.Run(() =>
{
throw new Exception();
return new ResponseType();
});
return await task;
}
public void Dispose()
{
}
}
public class TestCommunicatorGetsDependency :
ICommunicator<ResponseType, RequestType>
{
public TestCommunicatorGetsDependency()
{ }
public async Task<ResponseType> ProcessAsync(RequestType request)
{
TestDependancy = DefaultFactory.Default.Resolve<ITestDependancy>();
var task = Task.Run(() => new ResponseType());
return await task;
}
public ITestDependancy TestDependancy { get; set; }
public void Dispose()
{
}
}
[TestMethod]
[TestCategory("Unit")]
public async Task it_should_be_able_to_resolve_interface_from_original_thread()
{
var secondCommunicator = new TestCommunicatorGetsDependency();
_communicators = new ICommunicator<ResponseType, RequestType>[]
{new TestCommunicatorThrowsException(), secondCommunicator};
_communicatorServiceMock.Setup(
x => x.GetCommunicatorInstanceEnumerable(It.IsAny<string>(), It.IsAny<string>()))
.Returns(_communicators);
((IFactoryRegistrar) DefaultFactory.Default).RegisterPerThread<ITestDependancy, TestDependancy>();
var firstInstance = DefaultFactory.Default.Resolve<ITestDependancy>();
await it.VisitRequestAsync(_context).ConfigureAwait(true);
var secondInstance = secondCommunicator.TestDependancy;
Assert.AreEqual(firstInstance, secondInstance);
}
When the dependencies are resolved in the unit test they are not equal. After looking into it I see that the value for CurrentThread.ManagedThreadId changes at the point when the exception gets thrown. Then when it is caught in the VistRequestAsync method the CurrentThread.ManagedThreadId is never restored to its original state. So then the dependency injection is unable to get the same instance because it is now operating on a different thread.
Originally, I was using .ConfigureAwait(false) with the await. Then I tried setting it to true and I started seeing it sometimes get the same thread back. Which sounds a lot like what is said in this answer.
This post about the synchronization context and async sounds a lot like the problem I am facing. My trouble is I'm using WebApi and need a response back when things get done so I'm not sure how to use his message pump and asynchronously wait for an answer.
Async uses the ThreadPool to process tasks. This means that there is no guarantee that an async operation will start and complete on the same thread.
When a async task is first awaited, the task is put on a work queue. As soon as possible, the task scheduler grabs that task from the queue and assigns it to one of the many available threads.
For more information, see this overview of the structure of the TPL: https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx.
If you need a context that flows with the thread, look at using something like the logical call context or CallContext.LogicalSetData / LogicalGetData.
But the behavior you're seeing is correct, and as mentioned has nothing to do with whether or not an exception is thrown. You'll see different thread ids at various points of an asynchronous task's scheduling, execution, and completion.
I've been reviewing the code an app dev company has been doing for me in Xamarin. When I try to compile
namespace AppName.Droid
public class SplashActivity : AppCompatActivity
{
protected override void OnStart ()
{
base.OnStart ();
Task.Run (Login);
}
async void Login()
{
await LoadCurrentProfile().ContinueWith(result => {
if (ApplicationState.Profile== null)
{
GoLogin ();
}
else
{
GoBegin ();
}
});
}
}
There is an error on "Task.Run(Login)" that
'void SplashActivity.Login()' has the wrong return type
Is there something that I can change so that I can compile the app on my machine?
There's several things wrong with your approach to async. I recommend you read my async intro blog post and async best practices article.
In particular, you should follow these conventions:
Don't use ContinueWith. Use await instead.
Avoid async void. Use async Task unless you have to use async void.
Use Task.Run to push CPU-bound work off the UI thread. In the case of your code, there's no reason to use Task.Run.
Combining these, the code is quite simplified, and also has more reasonable semantics (in particular, login errors are not silently ignored):
protected override async void OnStart ()
{
base.OnStart();
await LoginAsync();
}
async Task LoginAsync()
{
await LoadCurrentProfile();
if (ApplicationState.Profile == null)
{
GoLogin();
}
else
{
GoBegin();
}
}
Change the signature of Login to return a Task like this:
async Task Login()
{
//...
}
And call it like this:
Task.Run( (Func<Task>) Login);
or
Task.Run(() => Login());
Please note that since Login is an asynchronous method, you can also call it directly without using Task.Run like this (I am assuming that you want to fire and forget):
Login();
In my project we have created action based method like below...which work as expected in our project.
public async Task MyMethod(Action<bool> SuccessAction, Action<Exception> ErrorAction)
{
try
{
SuccessAction(false);
}
catch (Exception ex)
{
ErrorAction(ex);
}
}
Now, for testing the above method below is how i have written the test method using NUnit.
[Test]
public async Task MyFirstTest()
{
var myClass = new MyClass();
await myClass.MyMethod(
Result =>
{
Assert.IsTrue(Result);//as all are aware that this will throw an exception.
},
Error =>
{
Assert.Fail();
});
}
Now, my question is when ever there is an exception occured at MyFirstTest then that exception get caught at the MyMethod.
I am not sure why this is happening.
Can any one please provide an solution to handle this.
Please let me know if more information is required or my question is not clear.