Refactoring My Repository Methods to be Asynchronous

Now I’ve written a DocumentDB based repository for Equipment data in DungeonMart, but I didn’t try to make it perfect, I just tried to make it work. Here it is, if you’re catching up: Part 1 and Part 2.

My next step in getting it closer to perfect is to make all the methods aynchronous. Face it, we’re going out to the cloud to get or write data and we shouldn’t make the whole service wait while that happens. DocumentDB gives you Async methods at least for writing, and we can figure out how to make the read methods async. If we were in Entity Framework, we would already have async methods for both reading and writing.

In a nutshell, what I’m about to do is make my repository methods async and then await the DocumentDB client calls inside the methods. This is pretty much exactly what you would do for Entity Framework, also. Here’s my old repository interface:

public interface IEquipmentRepository
{
    IEnumerable<Equipment> GetEquipments();
    Equipment GetEquipmentById(string id);
    Equipment AddEquipment(Equipment equipment);
    Equipment UpdateEquipment(string id, Equipment equipment);
    void DeleteEquipment(string id);
}

and here’s the new interface:

public interface IEquipmentRepository
{
    Task<IEnumerable<Equipment>> GetEquipmentsAsync();
    Task<Equipment> GetEquipmentByIdAsync(string id);
    Task<Equipment> AddEquipmentAsync(Equipment equipment);
    Task<Equipment> UpdateEquipmentAsync(string id,
        Equipment equipment);
    Task DeleteEquipmentAsync(string id);
}

You’ll notice that the methods return basically the same thing, just inside a task. I also changed the names of the methods to end with “Async.” This isn’t really a requirement of making them work, it’s just a convention so that consumers know they’re calling asynchronous methods. However, before we get to the methods, there was one tricky part I thought I should probably go over.

In the original, I saved the collection link in a static field that was initialized inline, like this:

private static readonly string CollectionLink =
    Client.GetOrCreateDocumentCollectionAsync("dmart","equipment")
    .Result.DocumentsLink

This was great for getting it working, but bad for long term. The call out to DocumentDB in Azure would happen at some indeterminate time before the first instance of the repository was constructed, and it would happen on the main thread. If the call took half a second, the main thread would have to wait.

I struggled with a lot of ways to make this better and settled on getting the collection link at the beginning of each method. Each method is going to be async, so we won’t be blocking anything if the call to get the collection link takes long. So I removed that line and added this line to the beginning of each CRUD method:

var collection = await Client
    .GetOrCreateDocumentCollectionAsync(Properties.DatabaseId,
        CollectionName);

We even get to await the call, meaning this thread can get out of the way while other threads work.

Now for the easy part. Make the methods async and await the things that are “awaitable.” For the read methods, it looks like this:

public async Task<Equipment> GetEquipmentByIdAsync(string id)
{
    var collection = await Client
        .GetOrCreateDocumentCollectionAsync(Properties.DatabaseId,
            CollectionName);
    return Client
        .CreateDocumentQuery<Equipment>(collection.DocumentsLink)
            .AsEnumerable().First(d => d.ID == id);
}

The write methods look very similar to how they did before, with a few tweaks:

public async Task<Equipment> AddEquipmentAsync(Equipment equipment)
{
    var collection = await Client
        .GetOrCreateDocumentCollectionAsync(Properties.DatabaseId,
            CollectionName);
    var result = await Client
        .CreateDocumentAsync(collection.DocumentsLink, equipment);
    dynamic doc = result.Resource;
    Equipment addedEquipment = doc;
    return addedEquipment;
}

Two things in the write methods are different than before. First, as previously discussed, I get the collection link in the method. Second, I wait for the CreateDocumentAsync call instead of forcing it to be synchronous.

When all is said and done, making my repository asynchronous turned out to be easier than I thought it would be, thanks to DocumentDB already providing me with async methods.

I’ve changed some other things in DungeonMart, too. All of the code as of this writing can be found in my github at this commit:

https://github.com/qanwi1970/dungeon-mart/tree/7b8f31b57c8575e14f84790abce0bbbd095cb4bc

One thought on “Refactoring My Repository Methods to be Asynchronous

Leave a comment