Today I'm pleased to announce the pre-release of our official .NET SDK! It's long overdue, but we've finally managed to get around to creating an officially supported SDK for all the .NET developers out there. As a token of appreciation, we're happy to announce that the .NET SDK also fully supports our Content Management API (CMA). This means that not only can you use it for delivering content to all your devices, but it also allows you to make edits and update content on the fly!
The SDK is built on top of the Microsoft .NET Standard Library 1.4 and .NET core. This means it's fully compatible with .NET core, .NET Framework 4.6.1 or later, Mono/Xamarin and Universal Windows Platform. Furthermore as it is built on top of .NET Core it is cross platform and can be used interchangeably on Windows, Linux or OSX. The SDK is available on NuGet
Install-Package contentful.csharp -Pre and is built with a modern, dependency injection friendly approach. It lets you, as a .NET developer, use your own classes to model your content. It also leverages asynchronous programming heavily which brings us to our next point...
The concept of asynchronous programming can at times seem arcane and mysterious; in fact, it's hard to reason about code when you don't know exactly in which order specific lines of code run. In .NET the whole process got a whole lot easier with the introduction of the
async methods in the Contentful .NET SDK. The aim of the rest of this article is to give you a better understanding of how asynchronous programming works in .NET and the benefits it brings to your code.
Why even bother? Why can't everything just run synchronously? The answer is of course that everything could run synchronously and the code would still work as expected, but let's look at this small example.
This method is not asynchronous and thus runs synchronously. The call to
GetEntry in turn calls methods on the
HttpClient and fetches a response from the Contentful API. This means that while we're waiting for the API response (quick as it might be there's still a significant latency in code execution terms) no other code can run on the thread. It is busy doing nothing but waiting for the response from the Contentful API. We could compare it to if you stand in line to order a burger and the cashier tells you your order will be done in a minute, and then just stands there staring at you for a full minute until your burger is ready. That's of course extremely inefficient. There must be a better way to use that minute of wasted time. This is basically what we address when we add
await to the mix. Let's rewrite our method slightly.
The addition of
await makes all the difference in the world. The code will now wait asynchronously for the result of
GetEntryAsync meaning that while it's waiting, the thread is free to do whatever other work it needs to do. It's like the cashier in our example above telling you that your order will be done in a minute, and then going on to serve the next person in line.
Note that this might not get you your hamburger faster, but it is a more efficient use of resources. The same thing is true for our
GetEntry call, making it asynchronously will not make the call return the result any faster, but makes sure we use our resources as efficiently as possible.
Unfortunately (or maybe fortunately) no, to be able to
await a method the result from that method needs to be awaitable. What does this mean? Well, it's quite complex, but it basically means the return type of the method needs to be able to capture an execution context and all sorts of very low-level things which are well beyond the scope of this article. Fortunately, there are two simple awaitables already created and ready to be used in the .NET framework.
Task<T>. If a method returns any of these two types, it can be awaited.
This leads us to an interesting conclusion. There must be a difference between the return type of
If you try to call the
GetEntryAsync method without using
await you'll end up with a
Task<T> and not the
Product class as you might've hoped. By placing the
await keyword before our method call the result will be the unwrapped result of
Task<T> to a
Product in our example.
If you have a method like this:
You'll notice that this code does not compile. The error message is The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task<Product>'. We have one more requirement to be able to use the
await operator: the method itself must itself be asynchronous. Thus we must rewrite our method like this.
The code will now compile just fine. We should also add the async suffix to our method to adhere to common practice.
This is all great, but it also means that any code calling our
GetProductByIdAsync method also needs to be asynchronous, at least to be able to use the
await operator (which you should).
Some people have referred to the async/await pattern as an infection that slowly consumes your entire code base. Although of course it's an exaggeration, introducing asynchronous methods does tend to force you to change more than just the calling method into an asynchronous one. While this can be tedious, the upside is that you are, with a few simple keywords, making sure your code is as performant and non-blocking as possible.
While the async/await keywords make asynchronous programming almost invisible to us, they also do increase the complexity under the hood. Let's again look at a simple example to better illustrate what we're talking about.
This somewhat useless function runs asynchronously, but is all of it asynchronous? In fact no, the code will run synchronously until it reaches the
await operator. Now,
await grabs the awaitable returned from the
DoSomethingWithSomeNumberAsync method and examines if it has already completed. If so it then just resumes code execution normally and synchronously. If it has not completed, however, it returns and exits the current method and tells the awaitable to execute the remainder of the method once it completes. This means that
i += 36; will not be executed until the
DoSomethingWithSomeNumberAsync is completed, thus giving us the illusion of synchronous code while being run asynchronously instead.
We could force our method to block while waiting for the asynchronous method. Let's look at an example in a web context. Imagine we have the following controller action.
Here we've gotten lazy and haven't made our method
async, instead we block the method by calling
.Result on our awaitable, effectively making our asynchronous calls useless as the thread is now being blocked waiting for the result of
GetEntryAsync to return. Not only does this make our call synchronous and our async calls further down the call chain superfluous, but it can also be a recipe for deadlocks.
async call in .NET is by default trying to keep track of the context in which it was called. In our Asp.NET example above this would be the
RequestContext of the current request. When the
async method returns, it tries to get ahold of this context again to make sure the remainder of the code gets executed in the same context as for where the method entered. This poses a real problem in the above example: when the
async method tries to get ahold of the
RequestContext, it's already busy waiting for the
Result of the asynchronous method, which in turn is waiting for the
RequestContext to become available. Deadlock!
Luckily the Contentful SDK mitigates this problem by disregarding the context in which it was called. It simply isn't necessary for the SDK to return the result of the calls in any specific context. This means that the code above actually does work when using the Contentful .Net SDK directly, but it's still not a good practice. Here's a better way to write that same method.
Non-blocking asynchronous code and no risk of deadlocks.
Of course, we could double the API surface of
IContentfulManagementClient by adding a blocking, synchronous method for each call. We would then have two methods for every call to the API, for example.
We do not believe in this. The Contentful SDK is at its core an asynchronous library. Every call through the SDK is routed to the
HttpClient and the
SendAsync method. By exposing two methods to chose from, we are hiding this fact from the user and providing a less performant way to make the same call, which we believe makes no real sense. It's also hard for us to make speculations about consuming code, therefore by keeping everything asynchronous, we force ourselves not to depend on the threading environment and make sure our SDK is as performant and lean as possible.
If a consumer of the SDK still wishes to call the methods synchronously this is of course entirely up to him or her, but then at least you do so with your eyes open and fully aware that you are blocking asynchronous methods in a potentially wasteful way.
This article has hopefully illustrated some of the important benefits of using asynchronous calls when calling external resources. It's not a simple matter to address, but the
async keywords make it a whole lot more approachable than a couple of years ago. They are to be considered an essential part of every .NET developer's toolbox. Yes,
async does tend to spread throughout your code once introduced and yes, wrapping every return type in a
Task<T> is not the prettiest thing in the world. However, the benefits far outweigh the costs in this case.
So go ahead, get the SDK, test it, and let us know if you have any feedback on how we could improve on it: open an issue or submit a pull request, we're happy to help! If you want to know more about
await, Contentful's .NET SDK, or talk about .NET in general, just hit me up on Twitter, I'll be happy to share the knowledge. And by the way, this is just the start for Contentful and .Net, we're planning on releasing more content in the upcoming weeks (did anyone say webinars?), so be sure to follow our official Twitter account @contentful, and you’ll be the first to know.