Pure.Krome
Pure.Krome

Reputation: 86937

How to call some async code in an ASP.NET application_start

In our application_startup, we seed up our database with some fake data, if no data exists.

To do this, we're using the Async methods to store the data. Great. Only problem is, we're not sure how to do this in the application_startup because that's not an async method.

I've spent soooo much time trying to understand @StevenCleary's tutorials and I'm always getting deadlocks. I totally grok what he consistently says:

As a general rule, you should use "async all the way down"; that is, don't block on async code

but I just don't get how I can do that, in this case :(

Lets imagine this is the code I'm trying to play with...

protected void Application_Start()
{
    var someFakeData = LoadSomeFakeData();
    var documentStore = new DocumentStore();
    await documentStore.InitializeAsync(someFakeData);

    ...

    // Registers this database as a singleton.
    Container.Register(documentStore);
}

and later on .. some code that uses this documentStore. It is injected via construction injection ...

public SomeController(IDocumentStore documentStore)
{
    _documentStore = documentStore;
}

public ViewModel GetFoos()
{
    using (var session = _documentStore.OpenSession())
    {
        ... db code goes in here ... 
    }
}

Clarification

I'm not trying to do some async code in here. I'm actually trying to call this async method, synchronously. Sure, i loose the benefits of async blah blah de blah.. but i'm happy with that. This is start up and I'm happy to block on startup.

Upvotes: 26

Views: 9811

Answers (4)

MikeM
MikeM

Reputation: 104

This is an old topic, but it's popped up in my search and maybe it will for others.

For what the OP has requested (ie. To run an async method in a synchronous way from inside a synchronous method, and block until it's finished), is there some reason that the use of Task.WaitAll would not be a simple and adequate way of addressing this?

protected void Application_Start()
{
    Task.WaitAll(MyAsyncMethod(true));
}

Upvotes: 2

Amir Astaneh
Amir Astaneh

Reputation: 2196

You can use of Task.Run(() => YourAsyncMethod()); inside of none async method like:

    protected void Application_Start()
    {
        Task.Run(() => MyAsyncMethod(true));
    }

Upvotes: 5

Vlad
Vlad

Reputation: 55

public static class AsyncHelper
{
    private static readonly TaskFactory MyTaskFactory = new
    TaskFactory(CancellationToken.None,
                TaskCreationOptions.None,
                TaskContinuationOptions.None,
                TaskScheduler.Default);

    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
    {
        return MyTaskFactory
          .StartNew(func)
          .Unwrap()
          .GetAwaiter()
          .GetResult();
    }
    public static void RunSync(Func<Task> func)
    {
        MyTaskFactory
          .StartNew(func)
          .Unwrap()
          .GetAwaiter()
          .GetResult();
    }
}

then use as

AsyncHelper.RunSync(ProcessAsync);

private async Task ProcessAsync(){ ....

Upvotes: -3

Stephen Cleary
Stephen Cleary

Reputation: 456322

In this case, you're asynchronously initializing a shared resource. So, I recommend that you either save the Task itself, or introduce an asynchronous wrapper type.

Using Task:

protected void Application_Start()
{
  var someFakeData = LoadSomeFakeData();
  var documentStore = new DocumentStore();
  var documentStoreTask = documentStore.InitializeAsync(someFakeData);

  ...

  // Registers this database task as a singleton.
  Container.Register(documentStoreTask);
}

That may be too awkward, though, depending on Container. In that case, you can introduce an asynchronous wrapper type:

public sealed class DocumentStoreWrapper
{
  private readonly Task<DocumentStore> _documentStore;

  public DocumentStoreWrapper(Data data)
  {
    _documentStore = CreateDocumentStoreAsync(data);
  }

  private static async Task<DocumentStore> CreateDocumentStoreAsync(Data data)
  {
    var result = new DocumentStore();
    await documentStore.InitializeAsync(data);
    ...
    return result;
  }

  public Task<DocumentStore> DocumentStoreTask { get { return _documentStore; } }
}

protected void Application_Start()
{
  var someFakeData = LoadSomeFakeData();
  var documentStoreWrapper = new DocumentStoreWrapper(someFakeData);

  ...

  // Registers this database wrapper as a singleton.
  Container.Register(documentStoreWrapper);
}

Or, you could use AsyncLazy<T>, which does much the same thing but uses a background thread to execute the initialization code.

Upvotes: 9

Related Questions