26

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.

6
  • 3
    Why not run this code synchronously instead? Using async IO is all about gaining throughput. This piece of code will run once and only on startup, i don't see the gain. Moreso, you'll have to put in more effort as this startup is completley synchronous. Does your provider have a synchronous API as well? Commented Feb 26, 2015 at 6:32
  • 1
    You can't really to my knowledge - unlike all request events this one does not have async version... You can use all methods to call async methods synchronously. Maybe lazy initialization on first actual request is an option? Commented Feb 26, 2015 at 6:33
  • 1
    There is no sync api avail. I understand that what i want to do is blocking. I get that and I'm happy with that -> this is some start up code .. call it once .. and then continue. I'm not trying to do any async here. I'm trying to do this as sync .. but don't know how. Commented Feb 26, 2015 at 23:35
  • 1
    are you using Entity Framework for your data layer ? if so, using Code-first initializers to seed the data will help you achieve seeding your database with your fake data. Commented Feb 26, 2015 at 23:47
  • 1
    No i'm not - and the database type should not be an issue - this question is agnostic to databases or anything else. It's about calling an async method in this sync code/pipeline. Commented Feb 27, 2015 at 0:15

4 Answers 4

9

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.

Sign up to request clarification or add additional context in comments.

12 Comments

Hi Stephen :) I was so hoping you might find this question and help :) Big thanks! What I don't understand with your answer is that you're still trying to avoid executing the method straight away? With the wrapper, that's still a Task that is being registered/remembered. With AsyncLazy<T>, that's still a Task being registered by it's it's also Lazy (which is nice!). But i still have to await it at some point? I was just hoping to run my init code, there and then (and yes.. block if i have to which I expect i do). Why can't i just do that - and that works without hanging.
@Pure.Krome: My solutions do start the method immediately. It is only awaited when the results are required.
I guess that's the part I'm really confused with :( :blush: I don't want a result here. I just want the method to execute, then stick the documentStore into the container. Looking at the code above, I see that i need to await to return the documentStore instance? I've updated my OP to show some example code how I use this documentStore, later on. I'm so sorry I'm struggle with this, Stephen but I really really do appreciate your kind help !
But that's my problem. I don't want to await anything else (eg methods or properties) from this instance. The only thing I wish to await (or more precisely, Wait) is the InitializeAsync() which should just happen on startup. Now I also grok the Lazy option here ... where the init will occur when the instance is FIRST requested/returned .. but I still don't see how that works here because AsyncLazy still returns a Task :( I know i really am missing something here, so please bear with me as I try and understand this :/ Please don't give up on me and this question :)
AFAIK, ASP.NET doesn't provide any way to asynchronously initialize. So your only real options are to either use synchronous methods (and block), or use a task to start the initialization on startup and await it later when you need it. Of the two, I prefer the latter if possible.
|
5

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

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

2 Comments

That should be the best answer. Simple and easy. And works like a charm.
The OP wanted to run the code synchronously and wait for it to complete. This approach runs the task in parallel via the threadpool. Not quite what he was looking for.
2

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));
}

Comments

-3
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(){ ....

1 Comment

This code will deadlock under certain circumstances. See Stephen Cleary's postings for details.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.