Wait for async Task without wrapping exceptions in AggregateException

ⅰ亾dé卋堺 提交于 2020-06-07 09:22:30

问题


I am using a library which provides methods ending with ...Async and return Task. I am going to use these in a command line application. So I need to call them synchronously a lot.

C# of course does not allow calling these methods in Main method since you cannot use async modifier on Main method. Assume this is the task:

var task = datastore.Save(data);

I found several solutions like:

Tasks.WaitAll(task);
task.Wait();

however all these wrap thrown exceptions in AggregateException, I don't want that. I just want to say task.Result and I expect the original exception to be thrown.

When I use a method returning Task<TResult>, task.Result throws AggregateException even though there are no continuation tasks set. Why is this hapening?

I also have tried,

task.RunSynchronously();

it gives error:

RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method.

so I guess that's not for methods marked as async.

Any ideas on patterns using libraries designed for async apps in console apps where there is no asynchronous context?


回答1:


I am going to use these in a command line application. So I need to call them synchronously a lot.

No, you don't. You can use async-await in a console application, you just need to make an async to sync transition at the very top. And you can do that by using Wait():

public static void Main()
{
    MainAsync().Wait();
}

public static async Task MainAsync()
{
    var datastore = …;
    await datastore.SaveAsync();
}

Usually, combining await with Wait() is a bad idea (it can cause deadlocks), but it's the right solution here.

Note that if SaveAsync() throws an exception and you don't catch it, it will be rethrown as AggregateException from the Wait(). But you can catch it as the original exception in MainAsync() (because it doesn't use Wait()).

If you really wanted to get the first exception thrown directly, you could do something similar to what await does: task.GetAwaiter().GetResult(). Note that if the Task contains more than one exception, you will get only the first one (but the same applies to await).

Since C# 7.1, you can make your Main method async and the compiler will write the transition code for you:

public static async Task Main()
{
    var datastore = …;
    await datastore.SaveAsync();
}

When I use a method returning Task<TResult>, task.Result throws AggregateException even though there are no continuation tasks set. Why is this happening?

This has nothing to do with continuations. A single Task can represent multiple operations, and each of them can throw an exception. Because of that, Task methods always throw the exceptions wrapped in an AggregateException.

I also have tried task.RunSynchronously()

That doesn't make any sense. RunSynchronously() can only be used on Tasks that were created using the Task constructor. That's not the case here, so you can't use it. Tasks returned from async methods are always already started.




回答2:


You can create a dummy Main

public static void Main()
{
    MainAsync().Wait();
}

public static async Task MainAsync()
{
    try {
        var result = await dataStore.Save(data);
    } catch(ExceptionYouWantToCatch e) {
       // handle it
    }
}

Also, see this answer: https://stackoverflow.com/a/9212343/1529246




回答3:


As of C# 7.1 you can now declare the Main method as async. Just have to make sure your language is either set to default major version (which should work in VS 2019 now) or you can always target a specific version of the language.

See this link for details. Turn on async main



来源:https://stackoverflow.com/questions/17667453/wait-for-async-task-without-wrapping-exceptions-in-aggregateexception

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!