Async methods in AppHost.Configure

I need to perform a lot of initialization when bootstrapping my servers, just BEFORE they start listening to incoming calls. Many of these methods I have written are asynchronous.

Some of my init code runs in the Configure method, some are called in the AfterInitCallback handler. Looks like I cannot await them. Here is a sample for my MongoDB initialization:

public override void Configure(Container container)
{
    MongoDbClient = new MongoClient(Settings.Default.LicenseServerConnStr);

    #region Redis database connection pools
    // .... code here       
    #endregion

    #region IOC registrations
    // ..... code here
    #endregion

    #region Plugin registrations
    // registering all required plugins
    #endregion

    #region ServiceStack Filters
    // ... code here
    #endregion

    #region Scheduler setup
    // code to init Quartz scheduler, contains ASYNC method calls
    #endregion

    #region RabbitMQ bus IOC registration
    // code to init RabbitMQ stuff
    #endregion


    AfterInitCallbacks.Add(host =>
    {
        // .. code to setup configuration  etc.
        RegisterMongoDbRepos(container);
        InitMongoDb();  // <<--- PROBLEM HERE, THIS NEEDS TO BE AWAITED!!

        RegisterScheduledJobs(container);
        RegisterMsgSubscriptions();
    });
}

The InitMongoDb()method calls tons of async methods of a generic MongoDB operations class I have written:

private async Task InitMongoDb()  //<<-- ASYNC method
{
    var db = MongoDbClient.GetDatabase(LsConstants.LsDatabaseName);
    var collectionNames = new List<string>();

    // .. other code ....
    try
    {
        var mongoOps = new MongoDbOperations(); <<--- THIS CLASS HAS TONS OF ASYNC METHODS
        if (!collectionNames.Contains(LsConstants.PushMessageStoreCollection))
        {
            db.CreateCollection(LsConstants.PushMessageStoreCollection,
                new CreateCollectionOptions
                {
                    Collation = new Collation("en", false, CollationCaseFirst.Off, CollationStrength.Primary)
                });
            // AWAITED method that creates lots of indexes etc on the database    
            await mongoOps.CreatePushMessageStoreCollectionIndexes(LsConstants.LicenseDatabaseName, OperationExecutionType.EventDriven);
        }
        // lot more things with async/await methods .....
    }
    catch (Exception collex)
    {
        Log.Error($"Error creating collections in database '{LsConstants.LicenseDatabaseName}'. Error: {collex}");
        throw;
    }
}

Is there any way to call async methods in the Configure and the callback handler? I tried

AfterInitCallbacks.Add(async host =>
{
    //...
    await InitMongoDb();
    // ...
});

This compiles but seems NOT to wait, my services are started BEFORE my init code has completed! Not what I want.

All Startup Initialization, e.g. Configure() is synchronous so you wont be able decorate into an async method. It’s important that the AppHost is initialized after Init() is called so the Web Server only starts spawning Worker threads to start processing requests after it’s properly initialized. Note async startup isn’t generally unsupported in all .NET Web Frameworks where .NET Core has decided against supporting Async startup.

Given you can’t call async/await inside Configure() you’d need to call it in a async method before initializing the AppHost, which you can do in an async Main method in C# 7 which should work for self-hosting .NET Core and HttpListenter Console Apps, e.g:

Program
{
    public static Collection MongoOps;

    public static async Task Main()
    {
        MongoOps = await MongoOpsAsync();
        //... 
    }
}

//...

new AppHost {
    MongoOps = Program.MongoOps
}

For ASP.NET it appears you need to implement an async wrapper hack, this blog post suggests you can make ASP.NET’s Global.asax async void Application_Start() but I’ve not seen anyone else doing it.

Then there’s the nuclear option of blocking on an async Wrapper method with .Wait() or .Result which is generally ill advised but depending on the impl, may not deadlock the App Startup Thread.

Hi Demis,
Thanks, interesting possibilities, didn’t know that .NET core is the ‘show-stopper’ here. Since in production all my servers will run on docker (Linux) I have to be .NET Standard 2.0 compatible wherever possible…

The problem with calling it from Main() is, that I use IOC inside my MongoOps class and the IOC container registration is in Configure() as well. So maybe I have to think about providing a Synchronous version of my MongoOps API even though I really don’t like that because it will contain a lot of duplicate code. But as we all know, ‘one size fits all’-solutions are not always possible… (and nuclear bombs I even hate more than duplicate code… :smile: )