DEV Community

Cover image for Asynchronous Programming in .Net Core (Part 1)
Fernando Sonego
Fernando Sonego

Posted on

Asynchronous Programming in .Net Core (Part 1)

We always want to create fast applications with great performance. For this reason, it is very important to know the asynchronous principles that .Net offers us. We will see the main concepts, common problems, complex scenarios, and what keywords that C # gives us to be able to work asynchronously.

Asynchronous programming in .NET

To begin with, it does not matter if we are not working in Asp.Net, in a console application, Windows Presentation Foundation, Windows Forms applications, or Xamarin, the asynchronous principles can be applied in any .Net environment.

Before looking at how it works in detail, let's look at an application that does not take advantage of these features. We will use a Windows Presentation Foundation application to see in detail what happens when having synchronous connections.

Our apps search for people by the color of their eyes. We have a TextBox and a Button to search for. Also, a grid where we will see the results, a text box to write, and a progress bar. Our application consults an API to retrieve the data. Let's see what happens.

The code that is executed when pressing the search button is the following:

.Net Async

var client = new WebClient();
var content = client.DownloadString($"https://localhost:44356/api/customers/search/" + search);
var data = JsonConvert.DeserializeObject<IEnumerable<Customer>>(content);

CustomersGrid.ItemsSource = data;

We are using a class that is now deprecated, which is WebClient. This class has the problem that it blocks the main thread, which is where the user interface is located, causing it to crash as well.

So what can we do? Well, first we will use a class that is designed to make use of asynchronous technology that is HttpClient. This class has the GetAsync method, and as its name indicates, it is an asynchronous operation, implying that it will not block the main thread where the interface is located.

try {
    using (var client = new HttpClient()) {

        var result = await client.GetAsync($"https://localhost:44356/api/customers/search/" + search);
        var content = await result.Content.ReadAsStringAsync();
        var data = JsonConvert.DeserializeObject<IEnumerable<Customer>>(content);

        CustomersGrid.ItemsSource = data;
    }
} catch (Exception ex) { 
}

We can see that the await keyword is found in our new code. This is the best way to indicate that we want to return to this part of the code once the data is loaded from the API we are querying. Let's see the behavior now that the code is written so as not to generate locks:

.Net Async

We can see our UI now is not blocked when consulting a service. We can also see the progress bar running without interruptions while waiting for the resolution of the data.

Actually, as you have surely noticed, with a few small changes in our application we can take advantage of the principles of asynchronism, providing a better user experience in this case.

Async and Await in .NET

Let's see the execution of the code where the button's click event is declared:

private async void button_Click(object sender, RoutedEventArgs e)
{

    try
    {
        using (var client = new HttpClient())
        {

            var result = await client.GetAsync($"https://localhost:44356/api/customers/search/" + search);
            var content = await result.Content.ReadAsStringAsync();
            var data = JsonConvert.DeserializeObject<IEnumerable<Customer>>(content);

            CustomersGrid.ItemsSource = data;
        }
     }catch (Exception ex) { }

}

Next to private appears the word async, this indicates that the event will be asynchronous, but it will not be enough for us to work correctly. For this, we must use the word await, which, as we commented previously, is in charge of notifying that it must wait until it is finished executing.

Suppose we only have the word async and never use await. Visual Studio will tell us that it will be waiting for the word await inside the method.

.Net Async

Another point that we must take into account is the method. As we can see the method is now called GetAsync (), in fact, it returns a task of the HttpResponseMessage type, we will talk later in the post, for now, we will only say that it will make it run in a different thread.

The asynchronous principles that we talk about in our applications are not only intended for Windows applications or mobile applications. These asynchronous principles are suitable for any type of I / O operations. For example, disk access, database access, memory or services as in the example.

We can also apply the same principle to server-side code in ASP.NET. Let's see the code of the rest API that is being invoked, which returns the list of people.

[Route("api/customers/search/{valor}")]       
public async Task<IList<Customer>> Get(string valor)
{
    List<Customer> items;

    using (StreamReader r = new StreamReader(@"customers.json"))
    {
        var json = await r.ReadToEndAsync();
        items = JsonConvert.DeserializeObject<List<Customer>>(json);
    }

    return items.Where(p => p.EyeColor == valor).ToList();
}

We have declared the controller method as asynchronous. Also, the type will be Task. This means that ASP.Net will know that there is an asynchronous operation in progress and then when all the results are available, they will be returned by the method.

The benefit that it offers us within ASP.Net is to reduce the excessive load on the web servers without interrupting the normal execution, allowing it to continue serving requests while it is waiting for the resolution of data or external accesses.

Understanding the flow

So far we have seen how to declare our methods as asynchronous, we saw the keywords as await and we understood how the operations are executed in a separate thread and not in the main thread of the applications.

Await allows us to retrieve the result of an asynchronous operation when it is available. It also assures us that there are no exceptions or problems with the currently running task. This means that it is not only the best way to wait, but also to validate the current operation.

This is where the term Continuation comes in. Continuation is the block of code that is executed after the await word.

.Net Async

As we can see in the image, our method called GetAsync (…) is marked with the word await, this means that what is marked in red is our Continuation block. Again, where the RadAsStringAsync () method is found, it is marked with await, this means that the next 2 lines would be our Continuation.

Let's make a change to test if there is a problem, for example, that it does not find any records. Our code would look like this:

using (var client = new HttpClient())
{
    var result = await client.GetAsync($"https://localhost:44356/api/customers/search/" + search);

    try {

        result.EnsureSuccessStatusCode();

        var content = await result.Content.ReadAsStringAsync();
        var data = JsonConvert.DeserializeObject<IEnumerable<Customer>>(content);

        CustomersGrid.ItemsSource = data;

        } catch (Exception ex) {
            txtOut.Text += ex.Message;
        }
}

Our method makes the asynchronous call but will not return an error. For this, we change our Try..Catch and inside we invoke result.EnsureSuccessStatusCode (). This validates the response code, as it is not 200 it will throw an exception. Finally, it will be caught by the catch by typing the error in the text box.

.Net Async

We can see that after the await word the code will be executed giving the feeling that we should not worry about working in another thread and that if it has a problem we can validate and retrieve the context of where we are executing our code.

Conclusion

In our applications, it is very important that we understand and begin to implement these asynchronous patterns. Not only because of the fact that our applications will have fewer blocks and will have a better user experience, but also that these blocks can produce additional costs. If we are working in the cloud we must not forget that the more we waste resources, the more we will have to pay. This is a good way to save costs with our applications.

In the next post, we will see how to create our own asynchronous methods, how to handle exceptions, and the best practices for it.

Top comments (0)