Friday, September 8, 2017

Using Task with .NET Core 2.0 (Success, Error, Cancellation)

Tasks are a key part of asynchronous programming in the .NET world -- this includes .NET Core 2.0 which was released last month. Today we'll take a look at consuming an asynchronous method (one that returns a Task) and see how to perform additional work after it has completed (a continuation), how to deal with exceptions, and also how to handle cancellation.

Initial Setup
In the previous 2 articles, we've seen how to create a WebAPI service and also a console application that consumes that service. Both of these are built with .NET Core 2.0. If you'd like to follow along, you can download the projects on GitHub:

WebAPI Service:
Console Application:

These projects have the completed code.

I'm using the command-line interface (CLI) in a bash terminal along with Visual Studio Code on macOS. But this will also work on Windows 10 using PowerShell and Visual Studio Code. My environment includes (1) the .NET Core 2.0 SDK, (2) Visual Studio Code, and (3) the C# Extension for Visual Studio Code. You can check the Microsoft .NET Core 2.0 announcement for resources on getting the environment set up.

We'll be expanding on the console application that was described in the previous article. We won't be so focused on the .NET Core environment as much as we'll be looking at how to use Task.

The Asynchronous Method
The asynchronous method that we're using is in the task-app-core project (a console application with a couple of class files). We'll start by opening the project folder in Visual Studio Code. This is the "TaskApp" folder that we worked with last time.

Here's the method signature of the asynchronous method (from the PersonRepository.cs file):

The important bit here is that "GetAsync()" returns a Task. A Task represents a concurrent operation. It may or may not happen on a separate thread, but we don't normally need to worry about those details.

In this case, we can treat "Task<List<Person>>" as a promise: at some point in the future, we will have a list of Person objects (List<Person>) that we can do something with.

The parameter is a CancellationToken. This allows us to notify the process that we would like to cancel.

We won't look at the insides of this method here. The only thing that we need to know is that this method has an artificial 3 second delay built in. This is so that we can see the asynchronous nature of the method.

This method gets data from a WebAPI service (person-api-core). We'll make sure that that service is running when we're ready to use it.

Running the Console Application
All of our work today will be done in the Program.cs file of the console application. The GitHub project has the completed code, but we'll be starting from scratch. If you want to follow along, you can get the GitHub files and then remove everything in the Program.cs file.

Here's how we'll start out:

This will print something to the console and then wait for us to press "Return" before it exits.

From the command line, just type

     dotnet run

to build and run the application:

If you see something different than this, it may be because the files are not saved. I'm used to Visual Studio automatically saving files whenever I build, so I've spent a bit of time trying to figure out why my code changes aren't working. It's usually because I forgot to save the files before running the application.

Starting the Service
Before going any further, let's fire up the service in another terminal window. To do this, we just need to navigate to the folder that contains the WebAPI project ("PersonAPI" from the prior article).

Then we just need to type

     dotnet run

to start the service:

Now we can find our data at http://localhost:9874/api/people.

Using a Task (Success Path)
Now we'll flip back to the console application (make sure you leave the service terminal window open). Let's consume the asynchronous method that we looked at above.

We need to create an instance of the "PersonRepository" class and then call the "GetAsync" method. Here's some initial code:

For now, I'll use "var result" to grab the return from the method. We'll give this a better name in just a bit. But before that, let's take a look at the parameter.

The "GetAsync" method needs a CancellationToken parameter, but we don't really care about cancellation at the moment. Rather than passing in a "null" or create a token that we won't use, we can use the static property "CancellationToken.None". This is designed so that we can just ignore cancellation.

Adding Using Statements
But when we type this into our code file, we see that "CancellationToken" does not resolve. In the .NET Core world, we create new class files that are empty; they don't have any "using" statements at the top. So we get to add them ourselves. But the C# Extension in Visual Studio Code will help us out a bit.

If we put our cursor on "CancellationToken", we see a lightbulb offering us help. You can press "Cmd+." on macOS (or "Ctrl+." on Windows, the same as Visual Studio) to activate the popup. You can also click on the lightbulb to get the popup:

The option "using System.Threading" will add the appropriate "using" statement to the top of our file. Since we're mostly starting with empty files, I find myself using this feature quite a bit.

Fixing the Result
I don't like using "var" to capture the return in this scenario because the return type isn't clear by just looking at the code. We can always hover our mouse over the "var" keyword to see the actual type, but I like the code to be readable without needing the extra assistance. So we'll change this to be more explicit with "Task<List<Person>>" and also by renaming "result" to "peopleTask".

This gives us a better idea of what is going on just by looking at the code.

Adding a Continuation
We can run the application now, but we'll get the same result that we had earlier. We're calling the "GetAsync" method, but we aren't doing anything to get the results out of it.

Task has a "Result" property, and it's really tempting to use it directly. The problem with using it directly is that it may not be populated yet. If we try to access the "Result" property before it is populated, then the current thread will be blocked until it is populated.

How do we get the result without blocking the current thread? We set up a continuation.

A continuation is a delegate that will run after the Task is complete. We can set this up with the "ContinueWith" method on the task. And this is where things get interesting:

Notice that the parameter for "ContinueWith" is "Action<Task<List<Person>>>". Yikes! That's a bit of a mouthful. (As a side note, there are many overloads for this method; we'll see another in a bit.)

"Action" represents a delegate that returns "void". In this case, the delegate takes one parameter of type "Task<List<Person>>". (For more information on Delegates, check out Get Func<>-y: Delegates in .NET.

To fulfill this delegate parameter, we just need to create a method that matches the method signature:

This method returns "void" and takes "Task<List<Person>>" as a parameter.

Inside this method, we can access the "Result" property safely. That's because this code will not run until *after* the Task has completed. So we know we won't inadvertently block the thread waiting for that. (We will still need to deal with exceptions, but we'll see that in a bit.)

"Result" is "List<Person>" in this case. I'm putting it into an explicit variable of that type to help with readability. After we have that, we just loop through the items and output them to the console. The "Person" class has a good "ToString()" method, so we don't have to do any custom formatting for basic output.

Then we just need to use "PrintNames" as the parameter for the "ContinueWith" call.

Here's our current "Program.cs":

This is enough to show our "success" path. To run the application, we'll go back to the command line (the one in the "TaskApp" folder).

Just type

     dotnet run

to see the result:

This console will display "One Moment Please..." and then after 3 seconds, it will list out the names that came from the service.

Just hit "Return" to exit the console application.

Because things are running asynchronously, the "ReadLine" method will run immediately. This means that if we press "Return" after "One Moment Please..." appears (but before the names appear), the application will exit and we will not see the names listed. (It will also exit if we press it *before* "One Moment Please..." appears because the console input gets cached until something uses it.)

Cleaning Things Up (and a Lambda Expression!)
I don't like the idea of needing to press "Return" to close the console application. If the data is printed successfully, then I'd like it to automatically exit. We can do this with the "Environment.Exit()" method:

The parameter is the exit code. "0" denotes no error, while other values are generally used to show that something went wrong.

Now when we run the application, we do not need to press "Return" after the names are printed:

Moving the Delegate to a Lambda Expression
The other thing I'd like to do is change the named delegate into a lambda expression. I've talked about lambda expressions many times, so I'll let you refer to those resources. In this case, the lambda expression is more-or-less an inline delegate.

Instead of using "PrintNames" as the parameter for "ContinueWith", we can use a lambda expression. The first part, "task", is the parameter for the delegate. This is the same parameter from "PrintNames", so it is a "Task<List<Person>>" (the compiler knows that so we don't need to type it explicitly). Then we have the lambda operator =>. And after that is the body of our lambda expression.

In this case, we can use the separate delegate or the in-lined lambda expression and get the same results. There are a few advantages to lambdas that are helpful with Task (such as captured variables). The main reason I encourage people to learn how to use lambdas here is because that's what you'll see in the wild when looking at code.

Breaking the Application
What we have now works -- as long as nothing goes wrong. Once we have something unexpected happen, things go a bit wonky.

Let's shut down the service to see how our application handles it. Just go back to the console window where the service is running and press "Ctrl+C" to stop the service:

Now when we run the console application, it just hangs:

Since it's hard to tell what's going on, we're going to use Visual Studio Code for some debugging. We'll go to the "Debug" menu and choose "Start with Debugging" (or press F5). This will stop on the actual error:

We can dig right into the middle and see that the error is "Couldn't connect to server". That makes sense since the server is no longer running.

But notice where we got this error. We actually get this error when we try to access the "Result" property on the Task. When an exception occurs inside Task code, the Task goes into a "faulted" state. When a Task is "faulted", then the "Result" property is invalid. So if we try to look at "Result" on a faulted Task, we'll get the exception instead.

Notice that the exception is an "AggregateException". This is a special exception that can hold other exceptions. If you'd like further details, you can take a look at this article: Task and Await: Basic Exception Handling.

Limiting Damage
We don't want to access the "Result" property on a faulted task, so we'll limit when our continuation runs. The "ContinueWith" method has many overloads, and a handy one takes "TaskContinuationOptions" as another parameter:

"TaskContinuationsOptions" is an enum. The "OnlyOnRanToCompletion" value means that this continuation will only run if the Task completes successfully. That takes care of our initial problem.

But our application still behaves the same way. If we run the application, it just hangs:

And if we use the debugger in Visual Studio Code, we don't see the exception since we're not doing anything to look for it.

Exception Handling (Error)
We've limited the damage, but we still want our application to behave nicely if something goes wrong. For this we'll add another continuation:

Now we have a continuation that's marked "OnlyOnFaulted". This will only run if the Task is in a faulted state. We can check the "Exception" property of the Task for details, but we'll just print out a "something went wrong" message here. For details on getting into the exception, check the Basic Exception Handling article mentioned above.

Another thing to note is that we're using an exit code of "1". We haven't defined what the different exit codes mean, but as noted, anything other than "0" denotes that something went wrong.

When we run the application now, it lets us know that something went wrong:

And if we start the service back up, we can see that the happy path still works for our application:

Is This Really Asynchronous?
It's hard to tell whether this code is really asynchronous. We saw above that if we pressed "Return" before the data returns that our application would exit. That shows us that things are still processing. But let's add a bit of code to see things more clearly.

At the bottom of the application, we'll add a few more "ReadLine"/"WriteLine" calls:

Now when we run our application, we can press "Return" three times, and we'll see the messages printed out:

If we press "Return" four times, then the application would exit. Let's add one more message to the code:

Then if we press "Return" four times, the application will exit before it prints out the names:

This is a bit of a simple demonstration, but it shows that our application is still running and processing input while the asynchronous method is running.

Stopping the Process Early (Cancellation)
The last thing we want to look at today is how to deal with cancellation. Our asynchronous method is already set up to handle cancellation -- that's what the CancellationToken parameter is for. On the consumer side, we need to be able to request cancellation and also deal with the results.

It's really tempting to simply new up a "CancellationToken" and pass it as a parameter to our method. But that will not work the way we want it to. That's because once we create a CancellationToken manually, there is no way for us to change the state. That's not very useful.

But we can use a "CancellationTokenSource" instead. This is an object manages a cancellation token.

Here's how we'll update our code:

At the top of our "Main" method, we create a new "CancellationTokenSource" object. Then when we call the "GetAsync" method, instead of passing in "CancellationToken.None", we'll pass in the "Token" property of our token source.

This gets the cancellation token to our asynchronous method. Now we need to set the token to a cancelled state.

For our console application, we'll give our user a chance to cancel the operation with the "X" key. We'll look for an "X" with the "ReadKey" method. This won't operate exactly how we would like, but we will clean that up in a little bit.

For now, we'll add a "ReadKey" just above our "ReadLine"/"WriteLine" methods:

If someone presses "X" (and it doesn't matter if it is upper case or lower case), then we call the "Cancel()" method on the CancellationTokenSource. This will put the cancellation token into a "cancellation requested" state. From there, it's up to the asynchronous method to figure out how it wants to handle that request.

Handling a Canceled Task
When a Task is canceled, it is put into a "canceled" state. This is different from the "faulted" state and the "ran to completion" state. With our current code, this means that we need another continuation to deal with cancellation.

Here's that continuation:

This is marked to run "OnlyOnCanceled", and we'll print a message to the output.

Testing Cancellation
With the pieces in place, we can run the application and try out cancellation. After "One Moment Please..." appears, press the "X" key. There will still be a 3 second pause because the asynchronous method doesn't check for cancellation until after the 3 second delay.

But then we should see our "canceled" message:

And if we don't press any keys, we'll see the success path:

This lets us see that cancellation is working, but we can't really get to our "Waiting..." logic anymore. We'll have to do some modification for that.

Better User Interaction
It's usually a challenge to get a good user experience with a console application, but we'll give it a shot. Here's the approach that I took for this code:

This replaces the previous "if (ReadKey..)" and "ReadLine"/"WriteLine" code. The endless loop lets us press "X" to cancel, "Q" to quit, and "Return" to get a "Waiting..." message. It's still a bit odd, but it handles the bulk of this application's needs.

The only other code update is to change the initial message:

Now we can test our interactivity. First, we'll press "X" for cancellation:

Then we'll try "Q" to quit:

And then we'll try "Return" a few times for the "Waiting..." message:

And if we do nothing, we'll still get the same results that we saw earlier.

Not a Ton of Code
We've implemented quite a bit of functionality here. We're consuming a method asynchronously; we're processing results when it completes, we're dealing with exceptions, and we can cancel the operation before it completes.

Here's the entire console application:

You can also look at this code on GitHub: Program.cs. The online code is a little different because I extracted out some separate methods for readability.

As an alternate, we can also create a single continuation and have code that branches based on the state of the Task (success, faulted, canceled). For more information, you can take a look at "Checking IsFaulted, IsCompleted, and TaskStatus".

Wrap Up
The good news is that using Task with .NET Core 2.0 is not much different from using it with the full .NET framework. The main challenges with porting over the full-framework code (available here: GitHub: jeremybytes/using-task) have to do with the UI demonstration. It's a bit easier to show some features when we have a desktop UI application, but we can still see the features in a console application.

Showing "await" with a console application is a bit more of a challenge. "Await" works just fine (with C# 7.1 which allows us to have an "async Main()" method), but since the code looks like blocking code, it makes it harder to show the asynchronous interactions. I'm still working on a demonstration for that.

If you want more information on Task and Await, be sure to check out my materials online: I'll Get Back to You: Task, Await, and Asynchronous Methods. This includes articles, code, videos, and much more. And also be on the lookout for my live presentations on the topic. I'm also available for workshops where we spend a full day on the topic of asynchronous programming.

I'm still exploring in the .NET Core 2.0 world. And I'm sure that there will be much more fun to come.

Happy Coding!

Tuesday, September 5, 2017

Consuming a WebAPI Service With .NET Core 2.0

Last time, we built a WebAPI service using .NET Core 2.0. This time, we'll write a console application/library that consumes it. Our goal today is to create a console project, make a service call, and use a NuGet package to help us parse the data.

This is part of the initial port of my Task sample code. Today we'll build the console application and get data from the service. In a later article, we'll explore Task in more detail.

Note: This code is available on GitHub: jeremybytes/task-app-core.
We'll also use the service created previously at GitHub: jeremybytes/person-api-core.

As with the service project, I'll be using the command-line interface (CLI) along with Visual Studio Code. I'm on macOS, but this will work the same on Windows 10. Also, as before, I'm starting with (1) the .NET Core SDK, (2) Visual Studio Code, and (3) the Visual Studio Code C# extension already installed. You can check the resources on the Microsoft announcement article to get the environment set up.

Our output isn't too exciting, but we will display data from the service in the terminal window:

Let's get started.

Initializing the Console Application Project
We'll start by building a new console application using .NET Core 2.0. For this, I created a folder for the project named "TaskApp". Once in that folder, we can type

     dotnet new console

to create a new console application project with the same name: TaskApp.csproj.

From here, we can open the "TaskApp" folder in Visual Studio Code.

When we open the folder, we get a popup to add some required assets:

When we click "Yes", Visual Studio Code will add a ".vscode" folder and a couple of files.

Here's how our initial file structure looks:

From here, we can do a build just as a sanity check that everything is working. Back in the terminal window we can use

     dotnet build

to build our console application.

It looks like everything is working so far.

Project Design Decisions
Visual Studio Code supports solutions that allow us to group multiple projects together. So why do we have the WebAPI service and console application completely separated?

This is mainly to make things easier to run. Without setting up any special build/run commands, we can simply navigate to the folder with the WebAPI service and type

     dotnet run

to start up the self-hosted service. Then we can leave this terminal window open (and the service running) while we work on the console application in a different terminal window.

It may not be the best solution, but it was easy for me to set up and keep track of what is running. I can also easily shut down the service to test the error handling of the application (as we'll see in a future article).

In addition, the full project that this code is based on uses a separate library for the repository. To keep things simple, we'll put the repository class right in the console application. As I get more comfortable with the environment, I may change my approach on this. But I think it works out fine for this sample.

Calling the Service
With the project set up, we'll need to write some code to call the service. But before that, we'll add the data class that describes a custom type.

We're dealing with "Person" objects, so we can add a "Person.cs" file to our project.

This class is available on GitHub: Person.cs, and it has the same members as the class in the service project. To keep things simple, we'll just copy the code over and change the namespace to match our console project.

I don't really like the idea of duplicating code from the service. This is something I'll be working to make a bit better in the future. For now it does get the job done.

Now that we have the type of our data, we can make the service call. This code can come almost straight across from the .NET 4.6 project that it is based on. There's just one change we really need to make.

We'll add a "PersonRepository.cs" file to our project and then put in the following code. (This is fairly similar to the completed PersonRepository.cs on GitHub.)

This code uses the HttpClient class to get data from our WebApi service. There's also a bit of asynchronicity added to demonstrate Task and await (including an artificial 3 second delay). We won't worry about those parts too much today; we'll look at them in the future.

Notice that we have some red squigglies. This is because "ReadAsAsync<T>" does not exist in the .NET Core library. Instead we'll need to do a bit of parsing ourselves.

And for that we'll use the Newtonsoft.Json NuGet package.

Adding a NuGet Package
To use Newtonsoft.Json, we'll need to bring the package into our project. We can do this from the command line with

     dotnet add package Newtonsoft.Json

One quirk about using this approach is that we need to know the exact name of the NuGet package. This isn't much different from using the NuGet command line tools in Visual Studio, but I don't know anyone who actually uses those regularly. Most folks I've come across (including myself) use the UI-based tools in VS. If needed, we can always search for the packages using

When we flip back to Visual Studio Code, we get prompted to resolve some dependencies.

Just click "Restore".

Deserializing Data
Now we just need to fix our code a bit to use the JSON deserializer. We need to add

     using Newtonsoft.Json

to the top of our file.

Then we use the JsonConvert object in our code:

The reason that we need this is because we do not have the "ReadAsAsync<T>" method available to us. But we do have "ReadAsStringAsync". This gives us the raw JSON that we can then parse using the "DeserializeObject<List<Person>>" method.

The Console Application
Now that we've added our new files (again, these are technically "library" files, but I've put them in the console project for simplicity), we can get to modifying our console application itself.

Here's the "Program.cs" file that was created for us:

Now we replace the contents of "Main" with our own code. (Note: this code is a bit simpler than what is in the completed Program.cs file on GitHub. We'll look at the details of using Task in a future article.)

This creates an instance of the "PersonRepository" class and then calls the "GetAsync" method. Since we're not dealing with cancellation yet, we can pass "CancellationToken.None" as the parameter. (We'll look at what that means when we take a closer look at Task.)

The "ContinueWith" method sets up a continuation, meaning code that will run when the asynchronous task completes. In addition, this continuation only runs if the task completes successfully (as denoted by "TaskContinuationOptions.OnlyOnRanToCompletion").

The continuation will print out the "Person" objects to the screen and then exit the console application.

We have a "Console.ReadLine()" method to make sure that the console application does not exit before the asynchronous process completes. But we can hit "Return" at any time to stop waiting.

Running the Application
With this in place, we can now run the application. Since this calls the WebAPI service, we need to make sure to start the service before running our console application.

To run the service, we navigate to the folder where the service project is located (which is "PersonAPI" based on the previous article) and type

     dotnet run

This will start the service at the location specified in the project.

We'll need to leave the service running, so we'll go to another terminal window to run the console application.

From a terminal in the "TaskApp" folder, we can use

     dotnet run

to kick off the console application. We do not need a separate "build" step here. Just like with our service, when we use "dotnet run" on a folder, it checks for launch instructions in the ".vscode" folder. By default, this includes a "build" step, so our code is always built before it is run. (Note: there are other ways to run the console application that do not require a build, but we won't look at that today.)

After the project builds, we'll get the initial output:

Then after 3 seconds, we get the results from the service:

The 3 second delay is an artificial one. It comes from the "await Task.Delay(3000)" that is in the "GetAsync" method. The delay is useful to help explore some asynchronous features such as cancellation and continued processing while the async method runs. (Again, we'll look at this in the next article.)

We don't have any error handling at the moment. so if we get an exception, then the console application will never exit.

If the console application hangs at "One Moment Please..." (meaning, it doesn't do anything after the 3 second delay), then it is most likely that the service is not running or that it is running on a different port.

You can double-check the service by navigating to http://localhost:9874/api/people in your browser. This should give the following result:

If this result doesn't appear and the service is running, then check the location listed in the terminal where it says "Now listening on:". If it has a different location, check the previous article for instructions on how to change that.

The Completed Project
Just like with our WebAPI project, we only need a few files for the console application. Here's what our folder looks like now:

Both the console application and WebAPI projects are available on GitHub.


The console application on GitHub has a bit more code to show cancellation, asynchronicity, and error handling. That will be our next stop.

Wrap Up
There's still a bit more to do with this code, but this gets us the basics for setting up a console application, consuming a web service, and even how to get NuGet packages into our .NET Core projects.

Stay tuned because there is still much more to explore.

Happy Coding!