Async and await in constructors

I was working on LIFTI last night, trying to create a Windows Store compatible version of it and encountered 2 main problems:

  • No support for IEnlistmentNotification, so no transactional support is possible, at least in the same way – I need to do a bit more digging to rule it out completely.
  • The file management class needed to initialise the backing file during construction. This proved to be a little challenging because all of the WinRT APIs for file management are asynchronous, and don’t mix particularly well with constructors.

The rest of this post will cover how I got around this without re-architecting the code extensively.

Moving to the WinRT world

The long and short of what the constructor does is create a read/write stream to the backing file – in the classic .NET world this is really simple:

var fileInfo = new FileInfo(filePath);
this.DataStream = fileInfo.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);

My initial “cheeky” attempt at getting this working with the WinRT APIs was something along the lines of:

var localFolder = ApplicationData.Current.LocalFolder;
var file =  localFolder.CreateFileAsync(
filePath,
CreationCollisionOption.OpenIfExists).GetResults(); this.DataStream = file.OpenStreamForWriteAsync().Result;

Which attempted to bypass the asynchronous nature of the APIs by reading the results inline and forcing the calls to execute synchronously. I didn’t really expect it to work, and sure enough it blew up at runtime – the CreateFileAsync line threw:

A method was called at an unexpected time. (Exception from HRESULT: 0x8000000E)

So the methods really do need to be awaited. But constructors can’t be async, so I ended up kicking off a new Task that ran an async delegate:

protected FileManagerBase(string fileName)
{
    Task.Run(
        async () =>
        {
            var localFolder = ApplicationData.Current.LocalFolder;
            var file = await localFolder.CreateFileAsync(
                fileName, 
                CreationCollisionOption.OpenIfExists);
            this.DataStream = await file.OpenStreamForWriteAsync();
        }).Wait();
}

Which seems to work, but I’d be really interested in hearing if this is frowned upon (other than the obvious “you’re blocking the calling thread”) and if there’s a better way of solving the problem.

2 Comments

  • Nate Diamond said

    One way that's been suggested before is instead of using a constructor, use the Factory pattern and so something like await FileManagerBaseFactory.CreateAsync(). Otherwise, you can do as you have done and make all of the accessor (methods) asynchronous. Then, you can create a Task for the construction and have all of the accessor methods await that first. It makes it much harder to use properties, unless you have the properties return Tasks as well (which you can do).

  • Mike said

    Hi Nate - the feedback is much appreciated. I've been meaning to update this post with a bigger health warning!

    I watched a couple of videos by Lucian Wischik on channel 9 recently - it turns out that the approach I described above is to be avoided if at all possible.

    When I get chance I'll have a look at this in LIFTI - I think I'm going to need an async Initialize method specifically for the WinRT version, which changes the API a little between the various versions, but there you go.

Comments have been disabled for this content.