Consider this code:
public async Task DoStuffAsync() {
await WhateverAsync(); // Stuff that might take a while
}
// Elsewhere in my project...
public async Task MyMethodAsync() {
await DoStuffAsync();
}
public void MyMethod() {
DoStuffAsync(); // <-- I want this to block until Whatever() is complete.
}
I want to provide two methods with the same functionality: an async method which runs asynchronously, and a non-async method that blocks, and either of these two methods can be called depending on whether the code I'm calling from is also async.
I don't want to repeat loads of code everywhere and create two versions of each method, wasting time and effort and reducing maintanability.
I'm guessing I can do something like one of the following:
// Call it just like any other method...
// the return type would be Task though which isn't right.
DoStuffAsync();
// Wrap it in a Task.Run(...) call, this seems ok, but
// but is doesn't block, and Task.Run(...) itself returns an
// awaitable Task... back to square one.
Task.Run(() => DoStuffAsync());
// Looks like it might work, but what if I want to return a value?
DoStuffAsync().Wait();
What is the correct way to do this?
UPDATE
Thanks for your answers. The general advice seems to be just provide an Async method and let the consumer decide, rather than creating a wrapper method.
However, let me try to explain my real-world problem...
I have a UnitOfWork
class that wraps the the SaveChanges() and SaveChangesAsync() methods of an EF6 DbContext
. I also have a List<MailMessage>
of emails that need to be sent only when the SaveChanges() succeeds. So my code looks like this:
private readonly IDbContext _ctx;
private readonly IEmailService _email;
private readonly List<MailMessage> _emailQueue;
// ....other stuff
public void Save() {
try {
_ctx.SaveChanges();
foreach (var email in _emailQueue) _email.SendAsync(email).Wait();
} catch {
throw;
}
}
public async Task SaveAsync() {
try {
await _ctx.SaveChangesAsync();
foreach (var email in _emailQueue) await _email.SendAsync(email);
} catch {
throw;
}
}
So you can see that because an EF6 DbContext
provides an async and non-async version, I kinda have to as well... Based on the answers so far, I've added .Wait()
to my SendAsync(email)
method, which asynchronously sends out an email via SMTP.
So... all good, apart from the deadlock problem... how do I avoid a deadlock?