<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Sergi Gisbert</title>
  <id>https://stage.sergigisbert.com/</id>
  <subtitle>Personal website - Sergi Gisbert | Software Architecht &amp; Developer</subtitle>
  <generator uri="https://github.com/sgisbert/Miniblog.Core" version="1.0">Miniblog.Core</generator>
  <updated>2021-02-28T12:01:29Z</updated>
  <entry>
    <id>https://stage.sergigisbert.com/blog/how-to-make-an-external-api-request-in-blazor-using-refit/</id>
    <title>How to make an external API request in Blazor using Refit</title>
    <updated>2021-02-28T17:55:21Z</updated>
    <published>2021-02-28T12:01:29Z</published>
    <link href="https://stage.sergigisbert.com/blog/how-to-make-an-external-api-request-in-blazor-using-refit/" />
    <author>
      <name>test@example.com</name>
      <email>Sergi Gisbert</email>
    </author>
    <category term="blazor" />
    <category term="dotnet core" />
    <category term="refit" />
    <category term="pwa" />
    <category term="english" />
    <content type="html">&lt;p&gt;&lt;strong&gt;Blazor&lt;/strong&gt; is the new development framework to develop SPA websites from Microsoft, and it is getting quite some attention lately. In this article, I'll do a simple demostration on how to make a request to an external API and displaying some data, using &lt;strong&gt;Refit&lt;/strong&gt; library in an easy way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Blazor&lt;/strong&gt;&amp;nbsp;allows to create website applications running &lt;strong&gt;C#&lt;/strong&gt;&amp;nbsp;on the client side, so effectively, most of the developers who consider ourselves mostly Backend devs, can now make the transition to become&amp;nbsp;&lt;strong&gt;Full-Stack&lt;/strong&gt;&amp;nbsp;without the need of learning another client side framework like Angular or React. Also, there are already several UI control libraries for Blazor, some even free like&amp;nbsp;&lt;a href="https://blazor.radzen.com/" target="_blank" rel="noopener"&gt;Radzen&lt;/a&gt;. My last real-world application I developed myself using Blazor can be browsed at&amp;nbsp;&lt;a href="https://www.saboraemocion.com/" target="_blank" rel="noopener"&gt;https://www.saboraemocion.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Making an introduction to Blazor is not the goal of this post, as there is already quite some good documentation on the topic to get you started pretty fast, and I recommend to start with the official docs and tutorials, at&amp;nbsp;&lt;a href="https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor" target="_blank" rel="noopener"&gt;https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor&lt;/a&gt;.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;What actually I'll be doing is to use a Blazor sample project to introduce you to &lt;a href="https://github.com/reactiveui/refit" target="_blank" rel="noopener"&gt;Refit&lt;/a&gt;, which is, quoting themselves "The automatic type-safe REST library for .NET Core, Xamarin and .NET", meaning it will automatically create an API client for us, starting from an Interface definition.&amp;nbsp;&lt;strong&gt;Refit&lt;/strong&gt;&amp;nbsp;will take care of the low-level details: HTTP requests, authetication, headers, serialization, type conversion, etc.&amp;nbsp;I will use Refit to create a small currency exchange conversion tool, with the data coming from the free API at&amp;nbsp;&lt;a href="https://api.exchangeratesapi.io/" target="_blank" rel="noopener"&gt;https://api.exchangeratesapi.io/&lt;/a&gt;&amp;nbsp;(you can see the service details at&amp;nbsp;&lt;a href="https://exchangeratesapi.io/" target="_blank" rel="noopener"&gt;https://exchangeratesapi.io/&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;The source code can be found at&amp;nbsp;&lt;a href="https://github.com/sgisbert/blazor-currencyexchange" target="_blank" rel="noopener"&gt;https://github.com/sgisbert/blazor-currencyexchange&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Initial configuration&lt;/h2&gt;
&lt;p&gt;For this simple example, we are creating a SPA application with &lt;strong&gt;Blazor Server&lt;/strong&gt;&amp;nbsp;and .Net 5. It can also be done using a model based on client and&amp;nbsp;&lt;strong&gt;Blazor WebAssembly&lt;/strong&gt;. Check the differences between both hosting models here:&amp;nbsp;&lt;a href="https://docs.microsoft.com/aspnet/core/blazor/hosting-models" target="_blank" rel="noopener"&gt;https://docs.microsoft.com/aspnet/core/blazor/hosting-models&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;To create the new application, we can make use of the Visual Studio wizard, or simply, as per the &lt;a href="https://dotnet.microsoft.com/learn/aspnet/blazor-tutorial/intro" target="_blank" rel="noopener"&gt;Blazor tutorial&lt;/a&gt;, run the following command from the console:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;dotnet new blazorserver -o BlazorApp&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;And to check that everything is working, we change folder to the "BlazorApp" created, and run:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;dotnet watch run&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this will compile the application, run it, and will keep watching the source code, to build and restart again every time a code change is made. To stop it, we just need to hit &lt;strong&gt;Ctrl&lt;/strong&gt;+&lt;strong&gt;C&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When it completes building, it will display that the app is listening to &lt;strong&gt;http://localhost:5001&lt;/strong&gt;&amp;nbsp;and our default browser will pop up on that address, showing the scaffolded app we just created:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image1_637495089046259032.png" alt="Image1.png" width="929" height="564" /&gt;&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Adding the business logic&lt;/h2&gt;
&lt;p&gt;The goal on this example will be listing the currency exchange values available for the current date. For that, we need to call the endpoint at&amp;nbsp;&lt;strong&gt;https://api.exchangeratesapi.io/latest&lt;/strong&gt;, that will return a JSON object like this:&amp;nbsp;&lt;/p&gt;
&lt;pre class="language-javascript"&gt;&lt;code&gt;{"rates":{"CAD":1.5307,"HKD":9.4121,"ISK":155.6,"PHP":58.846,"DKK":7.4368,"HUF":358.5,"CZK":25.849,"AUD":1.5445,"RON":4.8758,"SEK":10.033,"IDR":17094.81,"INR":88.0145,"BRL":6.5633,"RUB":89.6089,"HRK":7.578,"JPY":127.81,"THB":36.386,"CHF":1.0851,"SGD":1.6059,"PLN":4.483,"BGN":1.9558,"TRY":8.447,"CNY":7.8318,"NOK":10.2095,"NZD":1.6642,"ZAR":17.7391,"USD":1.2139,"MXN":24.7094,"ILS":3.97,"GBP":0.86508,"KRW":1339.59,"MYR":4.9048},"base":"EUR","date":"2021-02-19"}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where we have all the different exchange prices against Euro for each currency, for the last published date.&lt;/p&gt;
&lt;p&gt;The first thing I usually do is to create a new project on the solution to keep the business logic. This is not strictly necessary for this sample, but helps keeping up with best practices ;). To do so, I created a new .Net 5 library in the solution, called "Domain". In terms of "Domain Driven Development" (DDD) methodology, this will be our domain layer:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image3_637495089051827731.png" alt="Image3.png" width="343" height="80" /&gt;&lt;/p&gt;
&lt;p&gt;Again, full source code is on &lt;a href="https://github.com/sgisbert/blazor-currencyexchange/tree/4c7a62f6b07ccdcf406cee98fbe0afd50e87ed33" target="_blank" rel="noopener"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once we got the new project, we will start creating the data model to work with. To generate the model in C#, we can use a tool like &lt;a href="https://app.quicktype.io/" target="_blank" rel="noopener"&gt;https://app.quicktype.io/&lt;/a&gt;, which will generate a complete C# class&amp;nbsp; from the previous JSON data, optionally including helper methods to serialize and deserialize the data:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\image2_637495089052883030.png" alt="image2.png" width="1116" height="792" /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;We will add the generated code to class&amp;nbsp;&lt;strong&gt;Model/Rates.cs&amp;nbsp;&lt;/strong&gt;(we'll need to add a reference to the Nuget package&amp;nbsp;&lt;strong&gt;Newtonsoft.Json&lt;/strong&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;namespace Domain.Model
{
    using System;
    using System.Collections.Generic;

    using System.Globalization;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Converters;

    public partial class Rates
    {
        [JsonProperty("rates")]
        public Dictionary&amp;lt;string, double&amp;gt; RateList { get; set; }

        [JsonProperty("base")]
        public string Base { get; set; }

        [JsonProperty("date")]
        public DateTimeOffset Date { get; set; }
    }

    public partial class Rates
    {
        public static Rates FromJson(string json) =&amp;gt; JsonConvert.DeserializeObject&amp;lt;Rates&amp;gt;(json, Converter.Settings);
    }

    public static class Serialize
    {
        public static string ToJson(this Rates self) =&amp;gt; JsonConvert.SerializeObject(self, Converter.Settings);
    }

    internal static class Converter
    {
        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
        {
            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
            DateParseHandling = DateParseHandling.None,
            Converters = {
                new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
            },
        };
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Next, we are using &lt;strong&gt;Refit&lt;/strong&gt;&amp;nbsp;to define the service that will make use of the API. For that, first thing is to include the nuget package&amp;nbsp;&lt;strong&gt;Refit.HttpClientFactory&lt;/strong&gt;&amp;nbsp;in the project (&lt;span style="color: #808080;"&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: at the moment of writing this post, we are using Refit version 5.1.2. If you install version 6.x or higher it might not work straight away, because the default serializer engine has been changed from Newtonsoft.Json to System.Text.Json. I recommend to keep using 5.1.2 to follow the example without any issues, but have a look at the latest for new projects&lt;/em&gt;&lt;/span&gt;)&lt;/p&gt;
&lt;p&gt;We add the following Interface to the Domain project&amp;nbsp;&lt;strong&gt;Services/ICurrencyApi.cs&lt;/strong&gt;:&amp;nbsp;&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;using Domain.Model;
using Refit;
using System.Threading.Tasks;

namespace Domain.Services
{
    [Headers("Content-Type: application/json")]
    public interface ICurrencyApi
    {
        [Get("/latest")]
        Task&amp;lt;ApiResponse&amp;lt;Rates&amp;gt;&amp;gt; GetLatest();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Where we are defining the request to the "latest" endpoint from the API, which will return an object of type "&lt;strong&gt;ApiResponse&amp;lt;Rates&amp;gt;&lt;/strong&gt;", where &lt;em&gt;ApiResponse&lt;/em&gt;&amp;nbsp;will have the information of the request (http response code, errors, etc.) and the content of the response, which is our business model,&amp;nbsp;&lt;em&gt;Rates&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Next, we are adding the service that will make use of the Refit service,&amp;nbsp;&lt;strong&gt;Services/ICurrencyService.cs&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;using Domain.Model;
using Refit;
using System.Threading.Tasks;

namespace Domain.Services
{
    public interface ICurrencyService
    {
        Task&amp;lt;ApiResponse&amp;lt;Rates&amp;gt;&amp;gt; GetLatest();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;and its implementation,&amp;nbsp;&lt;strong&gt;Services/CurrencyService.cs&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;using Domain.Model;
using Refit;
using System.Threading.Tasks;

namespace Domain.Services
{
    public class CurrencyService : ICurrencyService
    {
        private readonly ICurrencyApi _api;

        public CurrencyService(ICurrencyApi api)
        {
            _api = api;
        }

        public async Task&amp;lt;ApiResponse&amp;lt;Rates&amp;gt;&amp;gt; GetLatest()
        {
            return await _api.GetLatest();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Finally, we will add a class to our library in charge of initializing the services we just created, so that netcore dependency injection is able to instantiate them correctly,&amp;nbsp;&lt;strong&gt;DomainStartup.cs&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;using Domain.Services;
using Microsoft.Extensions.DependencyInjection;
using Refit;
using System;

namespace Domain
{
    public static class DomainStartup
    {
        public static void InitDomain(this IServiceCollection services)
        {
            services.AddScoped&amp;lt;ICurrencyService, CurrencyService&amp;gt;();

            services.AddRefitClient&amp;lt;ICurrencyApi&amp;gt;()
                    .ConfigureHttpClient(c =&amp;gt; c.BaseAddress = new Uri("https://api.exchangeratesapi.io/"))
                    .SetHandlerLifetime(TimeSpan.FromMinutes(2));
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;In this case, we initialize our &lt;strong&gt;CurrencyService&lt;/strong&gt;, and make use of method&amp;nbsp;&lt;strong&gt;AddRefitClient&lt;/strong&gt;&amp;nbsp;to tell Refit the initialization values for the API, and the base URL of the service.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Using the service in the Blazor application&lt;/h2&gt;
&lt;p&gt;Until now, we have not modified anything on the Blazor application we created at the beginning. At this point, we will add some code to show the list of currencies and their exchange rate.&lt;/p&gt;
&lt;p&gt;First step will be modifying the &lt;strong&gt;Startup.cs&lt;/strong&gt;&amp;nbsp;to initialize the services from the Domain:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;using Domain;

...

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
    services.AddSingleton&amp;lt;WeatherForecastService&amp;gt;();

    services.InitDomain();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Next, we create a new Razor component to host the page with the list of currencies,&amp;nbsp;&lt;strong&gt;Pages/Currency.razor&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;@page "/currency"

@using Domain.Services
@using Domain.Model
@inject ICurrencyApi CurrencyService


&amp;lt;h1&amp;gt;Currency converter&amp;lt;/h1&amp;gt;

@if (rates == null)
{
    &amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Loading...&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;@error&amp;lt;/p&amp;gt;
}
else
{
    &amp;lt;h3&amp;gt;1 @rates.Base is worth as of @rates.Date.ToString("d"):&amp;lt;/h3&amp;gt;
    &amp;lt;table class="table"&amp;gt;
        &amp;lt;thead&amp;gt;
            &amp;lt;tr&amp;gt;
                &amp;lt;th&amp;gt;Currency&amp;lt;/th&amp;gt;
                &amp;lt;th&amp;gt;Rate&amp;lt;/th&amp;gt;
            &amp;lt;/tr&amp;gt;
        &amp;lt;/thead&amp;gt;
        &amp;lt;tbody&amp;gt;
            @foreach (var rate in rates.RateList)
            {
                &amp;lt;tr&amp;gt;
                    &amp;lt;td&amp;gt;@rate.Key&amp;lt;/td&amp;gt;
                    &amp;lt;td&amp;gt;@rate.Value&amp;lt;/td&amp;gt;
                &amp;lt;/tr&amp;gt;
            }
        &amp;lt;/tbody&amp;gt;
    &amp;lt;/table&amp;gt;
}

@code {
    private Rates rates;
    private string error;

    protected override async Task OnInitializedAsync()
    {
        var response = await CurrencyService.GetLatest();
        if (response.IsSuccessStatusCode)
        {
            rates = response.Content;
        }
        else
            error = response.Error.Content;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;In this page, we first inject our domain service:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;@inject ICurrencyApi CurrencyService&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;During the component initialization,&amp;nbsp;&lt;strong&gt;OnInitializedAsync()&lt;/strong&gt;, we call the service to get the data:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;var response = await CurrencyService.GetLatest();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;If the request is succesful, we initialize the local variable&amp;nbsp;&lt;strong&gt;rates&lt;/strong&gt;, which will be the one used to display the data on screen:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;rates = response.Content;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Finally, let's add a link in the menu to our newly created page, modifying&amp;nbsp;&lt;strong&gt;Shared/NavMenu.razor&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;&amp;lt;li class="nav-item px-3"&amp;gt;
    &amp;lt;NavLink class="nav-link" href="currency"&amp;gt;
        &amp;lt;span class="oi oi-list-rich" aria-hidden="true"&amp;gt;&amp;lt;/span&amp;gt; Currency
    &amp;lt;/NavLink&amp;gt;
&amp;lt;/li&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;If everything goes fine, and we still have the app running with "&lt;code&gt;dotnet watch run&lt;/code&gt;", we can go back to the browser and we will see the new option in the menu. If not, we just launch the application and wait for the browser to run. Clicking on the menu, we should see the list of currencies exchange rates at the current date:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image4_637495089054350184.png" alt="Image4.png" width="826" height="421" /&gt;&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Blazor is becoming a very interesting framework to develop SPA and dynamic web applications, same style as React, Angular, etc. specially for developers specialized on backend and C#.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refit&lt;/strong&gt;&amp;nbsp;is a library to dynamically create REST APIs clients with minimum effort, but very powerful at the same time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;In the following posts, I will keep showing some features from Blazor and RefitEn pr&amp;oacute;ximos posts seguir&amp;eacute; mostrando algunas funcionalidades de Blazor y Refit, going deeper a bit more on this sample application to calculate currency exchange rates.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://stage.sergigisbert.com/blog/como-hacer-una-peticion-a-un-api-externa-con-blazor-y-refit/</id>
    <title>Cómo hacer una petición a un API externa con Blazor y Refit</title>
    <updated>2021-02-21T17:01:30Z</updated>
    <published>2021-02-20T12:07:56Z</published>
    <link href="https://stage.sergigisbert.com/blog/como-hacer-una-peticion-a-un-api-externa-con-blazor-y-refit/" />
    <author>
      <name>test@example.com</name>
      <email>Sergi Gisbert</email>
    </author>
    <category term="blazor" />
    <category term="dotnet core" />
    <category term="refit" />
    <category term="pwa" />
    <content type="html">&lt;p&gt;&lt;strong&gt;Blazor&lt;/strong&gt; es el nuevo framework de desarrollo para crear SPA que ofrece Microsoft y que est&amp;aacute; teniendo mucho crecimiento en los &amp;uacute;ltimos tiempos. En este art&amp;iacute;culo haremos una peque&amp;ntilde;a demostraci&amp;oacute;n de c&amp;oacute;mo hacer una llamada a un API externa y mostrar los datos recibidos, utilizando &lt;strong&gt;Refit&lt;/strong&gt; de una manera muy sencilla.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Blazor&lt;/strong&gt; permite crear aplicaciones en el lado del cliente con &lt;strong&gt;C#&lt;/strong&gt;, por lo que consigue que muchos desarrolladores que nos consideramos m&amp;aacute;s de Backend, podamos dar el salto a &lt;strong&gt;Full-Stack&lt;/strong&gt; sin necesidad de aprender otros frameworks como Angular o React. As&amp;iacute;mismo, ya existen varias librer&amp;iacute;as de controles de UI para Blazor, incluso algunas gratuitas como las de &lt;a href="https://blazor.radzen.com/" target="_blank" rel="noopener"&gt;Radzen&lt;/a&gt;. La &amp;uacute;ltima aplicaci&amp;oacute;n real que he desarrollado con Blazor la pod&amp;eacute;is ver en&amp;nbsp;&lt;a href="https://www.saboraemocion.com/" target="_blank" rel="noopener"&gt;https://www.saboraemocion.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;No es el objetivo de este post hacer una introducci&amp;oacute;n a Blazor, ya que existe muy buena documentaci&amp;oacute;n y tutoriales oficiales que recomiendo seguir. Pod&amp;eacute;is empezar en&amp;nbsp;&lt;a href="https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor" target="_blank" rel="noopener"&gt;https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor&lt;/a&gt;.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;En cambio, voy a aprovechar un proyecto con Blazor para escribir sobre &lt;a href="https://github.com/reactiveui/refit" target="_blank" rel="noopener"&gt;Refit&lt;/a&gt;, una librer&amp;iacute;a que nos permite crear autom&amp;aacute;ticamente un cliente para consumir un API REST externa a partir de la definici&amp;oacute;n de un Interface. &lt;strong&gt;Refit&lt;/strong&gt; se encarga del resto: comunicaci&amp;oacute;n Http, autenticaci&amp;oacute;n, cabeceras, serializaci&amp;oacute;n, conversi&amp;oacute;n de tipos, etc. Usaremos Refit para crear un peque&amp;ntilde;o conversor de moneda, con los datos ofrecidos por la API gratuita&amp;nbsp;&lt;a href="https://api.exchangeratesapi.io/" target="_blank" rel="noopener"&gt;https://api.exchangeratesapi.io/&lt;/a&gt;&amp;nbsp;(ver detalles del servicio en &lt;a href="https://exchangeratesapi.io/" target="_blank" rel="noopener"&gt;https://exchangeratesapi.io/&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;El c&amp;oacute;digo fuente de ejemplo lo pod&amp;eacute;is encontrar en&amp;nbsp;&lt;a href="https://github.com/sgisbert/blazor-currencyexchange" target="_blank" rel="noopener"&gt;https://github.com/sgisbert/blazor-currencyexchange&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Configuraci&amp;oacute;n inicial&lt;/h2&gt;
&lt;p&gt;Para este ejemplo sencillo, crearemos una aplicaci&amp;oacute;n SPA con &lt;strong&gt;Blazor Server&lt;/strong&gt; con .Net 5. Tambi&amp;eacute;n se puede desarrollar con un modelo basado en cliente con &lt;strong&gt;Blazor WebAssembly&lt;/strong&gt;. Pod&amp;eacute;is ver las diferencias entre ambos modos de hospedaje en&amp;nbsp;&lt;a href="https://docs.microsoft.com/aspnet/core/blazor/hosting-models" target="_blank" rel="noopener"&gt;https://docs.microsoft.com/aspnet/core/blazor/hosting-models&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Para crear nuestra aplicaci&amp;oacute;n, podemos usar el asistente de Visual Studio, o simplemente, siguiendo los pasos del &lt;a href="https://dotnet.microsoft.com/learn/aspnet/blazor-tutorial/intro" target="_blank" rel="noopener"&gt;tutorial de Blazor&lt;/a&gt;, lanzar el siguiente comando desde PowerShell:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;dotnet new blazorserver -o BlazorApp&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y para comprobar que todo funciona, entramos en la carpeta "BlazorApp" que se ha generado y ejecutamos:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;dotnet watch run&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;que compilar&amp;aacute; y ejecutar&amp;aacute; la aplicaci&amp;oacute;n, y volver&amp;aacute; a compilar y reiniciarla cada vez que el c&amp;oacute;digo fuente cambie. Para detenerla basta con pulsar &lt;strong&gt;Ctrl&lt;/strong&gt;+&lt;strong&gt;C&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Cuando termine de compilar mostrar&amp;aacute; que est&amp;aacute; escuchando en http://localhost:5001 y se abrir&amp;aacute; el navegador con esa direcci&amp;oacute;n, mostrando la aplicaci&amp;oacute;n que acabamos de crear:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image1_637495089046259032.png" alt="Image1.png" width="929" height="564" /&gt;&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;A&amp;ntilde;adiendo la l&amp;oacute;gica de negocio&lt;/h2&gt;
&lt;p&gt;El objetivo de este ejemplo ser&amp;aacute; listar los valores de cambio de monedas disponibles para el d&amp;iacute;a de hoy. Para ello, haremos una llamada al endpoint&amp;nbsp;&lt;strong&gt;https://api.exchangeratesapi.io/latest&lt;/strong&gt;, que nos devuelve un objeto JSON como este:&amp;nbsp;&lt;/p&gt;
&lt;pre class="language-javascript"&gt;&lt;code&gt;{"rates":{"CAD":1.5307,"HKD":9.4121,"ISK":155.6,"PHP":58.846,"DKK":7.4368,"HUF":358.5,"CZK":25.849,"AUD":1.5445,"RON":4.8758,"SEK":10.033,"IDR":17094.81,"INR":88.0145,"BRL":6.5633,"RUB":89.6089,"HRK":7.578,"JPY":127.81,"THB":36.386,"CHF":1.0851,"SGD":1.6059,"PLN":4.483,"BGN":1.9558,"TRY":8.447,"CNY":7.8318,"NOK":10.2095,"NZD":1.6642,"ZAR":17.7391,"USD":1.2139,"MXN":24.7094,"ILS":3.97,"GBP":0.86508,"KRW":1339.59,"MYR":4.9048},"base":"EUR","date":"2021-02-19"}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donde tenemos los diferentes precios de cada moneda con respecto al Euro para la &amp;uacute;ltima fecha publicada.&lt;/p&gt;
&lt;p&gt;Lo primero que haremos ser&amp;aacute; crear un nuevo proyecto en la soluci&amp;oacute;n para albergar nuestra l&amp;oacute;gica de negocio. Esto no es imprescindible para este ejemplo, pero ayuda a mantener las buenas pr&amp;aacute;cticas ;). Para ello, creamos una nueva librer&amp;iacute;a basada en .Net 5 a la soluci&amp;oacute;n, y la llamaremos "Domain". En t&amp;eacute;rminos de la metodolog&amp;iacute;a "Domain Driven Development" (DDD), esta ser&amp;aacute; nuestra capa de Dominio:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image3_637495089051827731.png" alt="Image3.png" width="343" height="80" /&gt;&lt;/p&gt;
&lt;p&gt;El c&amp;oacute;digo fuente completo de este paso lo puedes encontrar en &lt;a href="https://github.com/sgisbert/blazor-currencyexchange/tree/4c7a62f6b07ccdcf406cee98fbe0afd50e87ed33" target="_blank" rel="noopener"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Una vez tenemos el nuevo proyecto, empezaremos creando el modelo de datos con el que vamos a trabajar. Para generar el modelo en C#, podemos usar una herramienta como&amp;nbsp;&lt;a href="https://app.quicktype.io/" target="_blank" rel="noopener"&gt;https://app.quicktype.io/&lt;/a&gt;, que a partir del JSON anterior nos va a generar una clase completa en C#, incluso con la opci&amp;oacute;n de a&amp;ntilde;adir m&amp;eacute;todos auxiliares para serializar y deserializar los datos:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\image2_637495089052883030.png" alt="image2.png" width="1116" height="792" /&gt;&lt;/p&gt;
&lt;p&gt;A&amp;ntilde;adimos el c&amp;oacute;digo generado a la clase &lt;strong&gt;Model/Rates.cs&amp;nbsp;&lt;/strong&gt;(necesitaremos a&amp;ntilde;adir una referencia al paquete de Nuget &lt;strong&gt;Newtonsoft.Json&lt;/strong&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;namespace Domain.Model
{
    using System;
    using System.Collections.Generic;

    using System.Globalization;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Converters;

    public partial class Rates
    {
        [JsonProperty("rates")]
        public Dictionary&amp;lt;string, double&amp;gt; RateList { get; set; }

        [JsonProperty("base")]
        public string Base { get; set; }

        [JsonProperty("date")]
        public DateTimeOffset Date { get; set; }
    }

    public partial class Rates
    {
        public static Rates FromJson(string json) =&amp;gt; JsonConvert.DeserializeObject&amp;lt;Rates&amp;gt;(json, Converter.Settings);
    }

    public static class Serialize
    {
        public static string ToJson(this Rates self) =&amp;gt; JsonConvert.SerializeObject(self, Converter.Settings);
    }

    internal static class Converter
    {
        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
        {
            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
            DateParseHandling = DateParseHandling.None,
            Converters = {
                new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
            },
        };
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A continuaci&amp;oacute;n, usaremos&amp;nbsp;&lt;strong&gt;Refit&lt;/strong&gt; para definir el servicio que realice el acceso a la API. Para ello, primero incluimos el paquete de Nuget&amp;nbsp;&lt;strong&gt;Refit.HttpClientFactory&lt;/strong&gt; en nuestra soluci&amp;oacute;n (&lt;span style="color: #808080;"&gt;&lt;em&gt;&lt;strong&gt;Nota&lt;/strong&gt;: en el momento de escribir este ejemplo, usamos la versi&amp;oacute;n 5.1.2 de Refit. Si instalas la versi&amp;oacute;n 6.x puede que no funcione correctamente porque se ha cambiado el motor de serializaci&amp;oacute;n por defecto de Newtonsoft.Json a System.Text.Json. Recomiendo instalar la versi&amp;oacute;n de este ejemplo para seguirlo sin problemas, pero usar la &amp;uacute;ltima para nuevos desarrollos&lt;/em&gt;&lt;/span&gt;)&lt;/p&gt;
&lt;p&gt;A&amp;ntilde;adimos el siguiente Interface al proyecto de Domain &lt;strong&gt;Services/ICurrencyApi.cs&lt;/strong&gt;:&amp;nbsp;&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;using Domain.Model;
using Refit;
using System.Threading.Tasks;

namespace Domain.Services
{
    [Headers("Content-Type: application/json")]
    public interface ICurrencyApi
    {
        [Get("/latest")]
        Task&amp;lt;ApiResponse&amp;lt;Rates&amp;gt;&amp;gt; GetLatest();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donde definimos la llamada al endpoint "latest" de la API, que nos va a devolver un objeto "&lt;strong&gt;ApiResponse&amp;lt;Rates&amp;gt;&lt;/strong&gt;", donde &lt;em&gt;ApiResponse&lt;/em&gt; contendr&amp;aacute; informaci&amp;oacute;n de la petici&amp;oacute;n (http response code, errores, etc. y el contenido de la misma, que es nuestro objeto de negocio, &lt;em&gt;Rates&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Seguidamente, a&amp;ntilde;adiremos el servicio que har&amp;aacute; uso del servicio de Refit, &lt;strong&gt;Services/ICurrencyService.cs&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;using Domain.Model;
using Refit;
using System.Threading.Tasks;

namespace Domain.Services
{
    public interface ICurrencyService
    {
        Task&amp;lt;ApiResponse&amp;lt;Rates&amp;gt;&amp;gt; GetLatest();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y su implementaci&amp;oacute;n,&amp;nbsp;&lt;strong&gt;Services/CurrencyService.cs&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;using Domain.Model;
using Refit;
using System.Threading.Tasks;

namespace Domain.Services
{
    public class CurrencyService : ICurrencyService
    {
        private readonly ICurrencyApi _api;

        public CurrencyService(ICurrencyApi api)
        {
            _api = api;
        }

        public async Task&amp;lt;ApiResponse&amp;lt;Rates&amp;gt;&amp;gt; GetLatest()
        {
            return await _api.GetLatest();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finalmente, a&amp;ntilde;adiremos una clase a nuestra librer&amp;iacute;a que se encargue de inicializar los servicios que hemos creado, para que la inyecci&amp;oacute;n de dependencias sea capaz de inicializarlos correctamente,&amp;nbsp;&lt;strong&gt;DomainStartup.cs&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;using Domain.Services;
using Microsoft.Extensions.DependencyInjection;
using Refit;
using System;

namespace Domain
{
    public static class DomainStartup
    {
        public static void InitDomain(this IServiceCollection services)
        {
            services.AddScoped&amp;lt;ICurrencyService, CurrencyService&amp;gt;();

            services.AddRefitClient&amp;lt;ICurrencyApi&amp;gt;()
                    .ConfigureHttpClient(c =&amp;gt; c.BaseAddress = new Uri("https://api.exchangeratesapi.io/"))
                    .SetHandlerLifetime(TimeSpan.FromMinutes(2));
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En este caso, inicializamos nuestro&amp;nbsp;&lt;strong&gt;CurrencyService&lt;/strong&gt;, y hacemos uso de la extensi&amp;oacute;n&amp;nbsp;&lt;strong&gt;AddRefitClient&lt;/strong&gt; para indicarle a Refit los par&amp;aacute;metros de inicializaci&amp;oacute;n del API, como la URL base del servicio.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Usamos la l&amp;oacute;gica en la aplicaci&amp;oacute;n Blazor&lt;/h2&gt;
&lt;p&gt;Hasta el momento no hemos modificado para nada la aplicaci&amp;oacute;n Blazor que hemos creado al principio. Ahora a&amp;ntilde;adiremos el c&amp;oacute;digo para mostrar el listado de monedas y su tipo de cambio.&lt;/p&gt;
&lt;p&gt;Lo primero que hacemos es modificar&amp;nbsp;&lt;strong&gt;Startup.cs&lt;/strong&gt; para inicializar los servicios que hemos creado en el Dominio:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;using Domain;

...

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
    services.AddSingleton&amp;lt;WeatherForecastService&amp;gt;();

    services.InitDomain();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A continuaci&amp;oacute;n, creamos un nuevo componente razor que albergar&amp;aacute; la p&amp;aacute;gina con el listado de monedas,&amp;nbsp;&lt;strong&gt;Pages/Currency.razor&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;@page "/currency"

@using Domain.Services
@using Domain.Model
@inject ICurrencyApi CurrencyService


&amp;lt;h1&amp;gt;Currency converter&amp;lt;/h1&amp;gt;

@if (rates == null)
{
    &amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Loading...&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;@error&amp;lt;/p&amp;gt;
}
else
{
    &amp;lt;h3&amp;gt;1 @rates.Base is worth as of @rates.Date.ToString("d"):&amp;lt;/h3&amp;gt;
    &amp;lt;table class="table"&amp;gt;
        &amp;lt;thead&amp;gt;
            &amp;lt;tr&amp;gt;
                &amp;lt;th&amp;gt;Currency&amp;lt;/th&amp;gt;
                &amp;lt;th&amp;gt;Rate&amp;lt;/th&amp;gt;
            &amp;lt;/tr&amp;gt;
        &amp;lt;/thead&amp;gt;
        &amp;lt;tbody&amp;gt;
            @foreach (var rate in rates.RateList)
            {
                &amp;lt;tr&amp;gt;
                    &amp;lt;td&amp;gt;@rate.Key&amp;lt;/td&amp;gt;
                    &amp;lt;td&amp;gt;@rate.Value&amp;lt;/td&amp;gt;
                &amp;lt;/tr&amp;gt;
            }
        &amp;lt;/tbody&amp;gt;
    &amp;lt;/table&amp;gt;
}

@code {
    private Rates rates;
    private string error;

    protected override async Task OnInitializedAsync()
    {
        var response = await CurrencyService.GetLatest();
        if (response.IsSuccessStatusCode)
        {
            rates = response.Content;
        }
        else
            error = response.Error.Content;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En esta p&amp;aacute;gina, primero estamos inyectando nuestro servicio de dominio:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;@inject ICurrencyApi CurrencyService&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Durante la inicializaci&amp;oacute;n del componente,&amp;nbsp;&lt;strong&gt;OnInitializedAsync()&lt;/strong&gt;, llamamos al servicio externo para obtener los datos:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;var response = await CurrencyService.GetLatest();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si la llamada ha sido correcta, inicializamos el valor de la variable local&amp;nbsp;&lt;strong&gt;rates&lt;/strong&gt;, que ser&amp;aacute; la que utilicemos para presentar los datos en pantalla:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;rates = response.Content;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Por &amp;uacute;ltimo, vamos a a&amp;ntilde;adir un enlace en el men&amp;uacute; a nuestra nueva p&amp;aacute;gina, modificando&amp;nbsp;&lt;strong&gt;Shared/NavMenu.razor&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;&amp;lt;li class="nav-item px-3"&amp;gt;
    &amp;lt;NavLink class="nav-link" href="currency"&amp;gt;
        &amp;lt;span class="oi oi-list-rich" aria-hidden="true"&amp;gt;&amp;lt;/span&amp;gt; Currency
    &amp;lt;/NavLink&amp;gt;
&amp;lt;/li&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si todo ha ido bien, y seguimos teniendo la aplicaci&amp;oacute;n lanzada con "&lt;code&gt;dotnet watch run&lt;/code&gt;", volvemos al navegador y veremos la nueva opci&amp;oacute;n en el men&amp;uacute;. Si no, lanzamos la aplicaci&amp;oacute;n y esperamos a que se cargue en el navegador. Al pulsar sobre el men&amp;uacute;, veremos nuestro listado de monedas frente al euro en la fecha de hoy:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image4_637495089054350184.png" alt="Image4.png" width="826" height="421" /&gt;&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Conclusiones&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Blazor se est&amp;aacute; convirtiendo en un framework muy interesante para el desarrollo de SPA y aplicaciones web din&amp;aacute;micas, al estilo de Angular, React, etc. especialmente para los desarrolladores especializados en backend y C#.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refit&lt;/strong&gt;&amp;nbsp;es una librer&amp;iacute;a para crear din&amp;aacute;micamente clientes de APIs REST sin ning&amp;uacute;n esfuerzo, pero muy potente a su vez.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En pr&amp;oacute;ximos posts seguir&amp;eacute; mostrando algunas funcionalidades de Blazor y Refit, profundizando un poco m&amp;aacute;s en este ejemplo de aplicaci&amp;oacute;n calcular los tipos de cambio entre monedas.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://stage.sergigisbert.com/blog/parallel-programming-in-c-with-class-parallel/</id>
    <title>Parallel programming in C# with class Parallel</title>
    <updated>2021-02-13T16:21:49Z</updated>
    <published>2021-02-13T15:14:08Z</published>
    <link href="https://stage.sergigisbert.com/blog/parallel-programming-in-c-with-class-parallel/" />
    <author>
      <name>test@example.com</name>
      <email>Sergi Gisbert</email>
    </author>
    <category term="c#" />
    <category term=".net" />
    <category term="dotnet core" />
    <category term="performance" />
    <category term="tips" />
    <category term="parallel" />
    <category term="english" />
    <content type="html">&lt;p&gt;&lt;em style="color: #808080;"&gt;Disclaimer: the methods and code examples in this article are the fruit of my own investigation and self learning, and in no means these are ready to be used in a real, production environment. Use at your own risk. To learn more about parallel and synchronous programming, I recommend reading the official documentation at:&amp;nbsp;&lt;a href="https://docs.microsoft.com/dotnet/standard/parallel-processing-and-concurrency" target="_blank" rel="noopener"&gt;https://docs.microsoft.com/dotnet/standard/parallel-processing-and-concurrency&lt;/a&gt;&amp;nbsp;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;This post is part of a series of 3 articles about performace improvement with parallel programming:&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;1. &lt;a style="color: #993300;" href="https://www.sergigisbert.com/blog/improve-the-performance-with-asynchronous-functions-to-run-processes-in-parallel/" target="_blank" rel="noopener"&gt;Improve the performance with asynchronous functions to run processes in parallel&lt;/a&gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;2. &lt;a style="color: #993300;" href="https://www.sergigisbert.com/blog/make-use-of-concurrentbag-to-store-the-results-from-asynchronous-processes/" target="_blank" rel="noopener"&gt;Make use of ConcurrentBag to store the results from asynchronous processes&lt;/a&gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;3. Parallel programming in C# with class Parallel (this article)&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Following up with the series of posts about async programming and performance improvements using Tasks for process parallelization, in this one I am introducing a new option, using &lt;em&gt;Parallel&lt;/em&gt; class available in .Net, and I will compare it with the previous examples, where we run a process that returns a list of 10 random numbers between 1 and 100, and we cut it down to&amp;nbsp;&lt;strong&gt;0,6s&lt;/strong&gt;&amp;nbsp;execution time, from the starting&amp;nbsp;&lt;strong&gt;2s&lt;/strong&gt;&amp;nbsp;of the synchronous process:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;Result: 82,83,88,99,57,71,13,54,18,40
Completed: 00:00:00.6276547&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can find the source code of this examples on Github:&amp;nbsp;&lt;a href="https://github.com/sgisbert/parallelization" target="_blank" rel="noopener"&gt;https://github.com/sgisbert/parallelization&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At this examples, we used&amp;nbsp;&lt;em&gt;Task.Run()&lt;/em&gt;&amp;nbsp;to execute a process in a new thread. It is important to understand that the use of&amp;nbsp;&lt;em&gt;Task&lt;/em&gt;&amp;nbsp;to create new threads is limited to the number of cores available, and the excesive use of these can lead to a not so efficient use of system resources. To better understand these concepts, I recommend further reading:&amp;nbsp;&lt;a href="https://www.pluralsight.com/guides/using-task-run-async-await#module-knowthenatureofyourasynchronousoperation" target="_blank" rel="noopener"&gt;https://www.pluralsight.com/guides/using-task-run-async-await#module-knowthenatureofyourasynchronousoperation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The use of&amp;nbsp;&lt;em&gt;async/await&lt;/em&gt;&amp;nbsp;pattern is specially recommended with libraries or API calls that already offer async methods, without the need to use &lt;em&gt;Task.Run()&lt;/em&gt;&amp;nbsp;to create them. Good examples of this are &lt;em&gt;HttpClient&lt;/em&gt;&amp;nbsp;or&amp;nbsp;&lt;em&gt;FileStream&lt;/em&gt;, that offer I/O async methods and we can take full benefit of async programming.&lt;/p&gt;
&lt;p&gt;If still we have a use case like in the example, where you need to execute the same logic over several objects, we can use the class&amp;nbsp;&lt;em&gt;Parallel&lt;/em&gt;, and see how it performs, in comparison with &lt;em&gt;Task.WaitAll()&lt;/em&gt;&amp;nbsp;or&amp;nbsp;&lt;em&gt;Task.WhenAll().&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Task.WaitAll() vs Task.WhenAll()&lt;/h2&gt;
&lt;p&gt;There are two different ways to wait for all our tasks to complete:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;await Task.WhenAll(tasks.ToArray());&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and&amp;nbsp;&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;Task.WaitAll(tasks.ToArray());&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and it is interesting to know the differences between both. In terms of performance, the process takes a very similar amount of time to complete: 0,6s, but there are some aspects worth to be noted:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Task.WaitAll()&lt;/em&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The main UI execution thread is blocked until all the tasks are completed, so it does not return a&amp;nbsp;&lt;em&gt;Task&lt;/em&gt;. This means that if we have a UI dependant application, it could freeze until the execution ends.&lt;/li&gt;
&lt;li&gt;If there are any exceptions thrown, it will throw an &lt;em&gt;AggregateException&lt;/em&gt; with all the exceptions together.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;await Task.WhenAll()&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The main thread is not blocked, so this would be the recommended approach for a UI application and async methods, as it actually returns a&amp;nbsp;&lt;em&gt;Task&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;If there are any exceptions, only the first one is returned.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this article, you can explore deeper the differences between both methods:&amp;nbsp;&lt;a href="https://www.infoworld.com/article/3152735/when-to-use-taskwaitall-vs-taskwhenall-in-net.html" target="_blank" rel="noopener"&gt;https://www.infoworld.com/article/3152735/when-to-use-taskwaitall-vs-taskwhenall-in-net.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Using Parallel class&lt;/h2&gt;
&lt;p&gt;Another way to make use of parallel programming advantages, is with class&amp;nbsp;&lt;em&gt;Parallel&lt;/em&gt;&amp;nbsp;in .Net (more info in the&amp;nbsp;&lt;a href="https://docs.microsoft.com/dotnet/api/system.threading.tasks.parallel?view=net-5.0" target="_blank" rel="noopener"&gt;official documentation&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;First thing to do with our code will be to transform the process so it does not need to use&amp;nbsp;&lt;em&gt;Task.Run()&lt;/em&gt;, but keeping the compatibility with&amp;nbsp;&lt;em&gt;Task&lt;/em&gt;&amp;nbsp;to be able to compare both results. So, let's modify the method in the following way (&lt;a href="https://github.com/sgisbert/parallelization/blob/540ac5c9138c8549ee1ce87efb74948424309abc/src/Program.cs" target="_blank" rel="noopener"&gt;see full source code on GitHub&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;private static async Task Process(int id, ConcurrentBag&amp;lt;int&amp;gt; cb)
{
    await Task.Run(() =&amp;gt;
    {
        CoreProcess(id, cb);
    });
}

private static void CoreProcess(int id, ConcurrentBag&amp;lt;int&amp;gt; cb)
{
    Stopwatch timer = new Stopwatch();
    timer.Start();
    Random random = new Random();
    Thread.Sleep(200);

    int number = random.Next(1, 100);
    cb.Add(number);

    Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] Process {id}: {timer.Elapsed}");
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also added the value of&amp;nbsp;&lt;em&gt;Thread.CurrentThread.ManagedThreadId&lt;/em&gt;&amp;nbsp;to the console output to check the Threads being created.&lt;/p&gt;
&lt;p&gt;This way, we can call our process both in sync or async way.&lt;/p&gt;
&lt;h3&gt;Parallel.ForEach()&lt;/h3&gt;
&lt;p&gt;Now we can add the following code, using&amp;nbsp;&lt;a href="https://docs.microsoft.com/dotnet/api/system.threading.tasks.parallel.foreach?view=net-5.0" target="_blank" rel="noopener"&gt;&lt;em&gt;Parallel.ForEach()&lt;/em&gt;&lt;/a&gt;&amp;nbsp;to run the synchronous process, but being parallelized (&lt;a href="https://github.com/sgisbert/parallelization/blob/04c243d2b4b50d69315fbd562bd2739f95627374/src/Program.cs" target="_blank" rel="noopener"&gt;see full source code on GitHub&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;timer.Restart();
ConcurrentBag&amp;lt;int&amp;gt; cb2 = new ConcurrentBag&amp;lt;int&amp;gt;();
var sourceCollection = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Parallel.ForEach(sourceCollection, (id) =&amp;gt;
{
    CoreProcess(id, cb2);
});
Console.WriteLine($"Result: {string.Join(",", cb2)}");
Console.WriteLine($"Completed: {timer.Elapsed}");
Console.WriteLine();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And let's compare the results:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Task.WaitAll()&lt;/em&gt;&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;[6] Process 2: 00:00:00.2028313
[5] Process 1: 00:00:00.2039001
[4] Process 0: 00:00:00.2063105
[7] Process 3: 00:00:00.2027921
[4] Process 5: 00:00:00.2006033
[7] Process 7: 00:00:00.2006397
[5] Process 6: 00:00:00.2006732
[6] Process 4: 00:00:00.2007073
[7] Process 8: 00:00:00.2001104
[5] Process 9: 00:00:00.2009985

Result: 15,49,52,76,90,1,71,67,37,23
Completed: 00:00:00.6398704
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Parallel.ForEach()&lt;/em&gt;&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;[6] Process 9: 00:00:00.2011420
[5] Process 3: 00:00:00.2012138
[1] Process 1: 00:00:00.2013376
[4] Process 7: 00:00:00.2011324
[7] Process 5: 00:00:00.2011974
[1] Process 2: 00:00:00.2004285
[4] Process 8: 00:00:00.2004063
[7] Process 6: 00:00:00.2005255
[6] Process 10: 00:00:00.2006825
[5] Process 4: 00:00:00.2006454
Result: 50,82,38,39,37,96,80,48,95,90
Completed: 00:00:00.4507518&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The task run with &lt;em&gt;Parallel&lt;/em&gt;&amp;nbsp;has decreased the time to&amp;nbsp;&lt;strong&gt;0,45s&lt;/strong&gt;&amp;nbsp;in front of the&amp;nbsp;&lt;strong&gt;0,6s&lt;/strong&gt;&amp;nbsp;of&amp;nbsp;&lt;em&gt;Task&lt;/em&gt;. We got a performance improvement without having to make use of &lt;em&gt;Task.Run(),&lt;/em&gt;&amp;nbsp;which could bring up potential problems with the use of system resources.&lt;/p&gt;
&lt;h3&gt;Parallel.Invoke()&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Parallel&lt;/em&gt;&amp;nbsp;offers also another alternative to execute parallel tasks, with method&amp;nbsp;&lt;em&gt;&lt;a href="https://docs.microsoft.com/dotnet/api/system.threading.tasks.parallel.invoke?view=net-5.0" target="_blank" rel="noopener"&gt;Invoke()&lt;/a&gt;&lt;/em&gt;. To test it, let's add the following code lines (&lt;a href="https://github.com/sgisbert/parallelization/blob/6019085440710a0345fe2ed2f4bc3c3a1f414619/src/Program.cs" target="_blank" rel="noopener"&gt;see full source code on Github&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;timer.Restart();
ConcurrentBag&amp;lt;int&amp;gt; cb3 = new ConcurrentBag&amp;lt;int&amp;gt;();
List&amp;lt;Action&amp;gt; actions = new List&amp;lt;Action&amp;gt;();
foreach (var i in sourceCollection)
{
    actions.Add(() =&amp;gt; CoreProcess(i, cb3));
}
Parallel.Invoke(actions.ToArray());
Console.WriteLine($"Result: {string.Join(",", cb3)}");
Console.WriteLine($"Completed: {timer.Elapsed}");&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the following results:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;[4] Process 3: 00:00:00.2008078
[6] Process 4: 00:00:00.2008435
[5] Process 5: 00:00:00.2007954
[7] Process 2: 00:00:00.2008161
[1] Process 1: 00:00:00.2009441
[7] Process 8: 00:00:00.2001254
[5] Process 6: 00:00:00.2001896
[4] Process 7: 00:00:00.2001298
[8] Process 10: 00:00:00.2033235
[6] Process 9: 00:00:00.2089627
Result: 55,26,74,29,32,45,71,46,39,90
Completed: 00:00:00.4121676&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case,&amp;nbsp;&lt;em&gt;Invoke()&lt;/em&gt;&amp;nbsp;reduces the time up to nearly&amp;nbsp;&lt;strong&gt;0,4s&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Analize deeply each situation, understanding how async methods work and the differences between&amp;nbsp;&lt;em&gt;Task.WaitAll&lt;/em&gt;&amp;nbsp;y&amp;nbsp;&lt;em&gt;Task.WhenAll&lt;/em&gt;, to make use of them correctly on every situation.&lt;/li&gt;
&lt;li&gt;Know other alternatives like &lt;em&gt;Parallel&lt;/em&gt;&amp;nbsp;to improve the application performance.&lt;/li&gt;
&lt;li&gt;Don't take any of these examples for granted. Make your own performance tests and check on every case what solution works best for you&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://stage.sergigisbert.com/blog/programacion-paralela-en-c-con-la-clase-parallel/</id>
    <title>Programación paralela en C# con la clase Parallel</title>
    <updated>2021-02-13T16:15:58Z</updated>
    <published>2021-02-07T11:59:45Z</published>
    <link href="https://stage.sergigisbert.com/blog/programacion-paralela-en-c-con-la-clase-parallel/" />
    <author>
      <name>test@example.com</name>
      <email>Sergi Gisbert</email>
    </author>
    <category term="c#" />
    <category term="dotnet core" />
    <category term="performance" />
    <category term="tips" />
    <category term="parallel" />
    <content type="html">&lt;p&gt;&lt;span style="color: #808080;"&gt;&lt;em&gt;Disclaimer: los m&amp;eacute;todos y ejemplos de c&amp;oacute;digo de este art&amp;iacute;culo son fruto de mi propia investigaci&amp;oacute;n y aprendizaje aut&amp;oacute;nomo, y en ning&amp;uacute;n caso est&amp;aacute;n preparados para su utilizaci&amp;oacute;n en c&amp;oacute;digo real de producci&amp;oacute;n. El autor no se hace responsable del uso de estos ejemplos. Para m&amp;aacute;s informaci&amp;oacute;n sobre programaci&amp;oacute;n paralela y as&amp;iacute;ncrona y sus connotaciones, recomiendo acceder a la documentaci&amp;oacute;n oficial: &lt;a style="color: #808080;" href="https://docs.microsoft.com/dotnet/standard/parallel-processing-and-concurrency" target="_blank" rel="noopener"&gt;https://docs.microsoft.com/dotnet/standard/parallel-processing-and-concurrency&lt;/a&gt;&amp;nbsp;&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;Este post forma parte de una serie de 3 art&amp;iacute;culos sobre mejoras de rendimiento utilizando programaci&amp;oacute;n paralela:&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;1.&amp;nbsp;&lt;a style="color: #993300;" href="https://www.sergigisbert.com/blog/mejorar-el-rendimiento-con-funciones-asincronas-para-la-ejecucion-de-procesos-en-paralelo/" target="_blank" rel="noopener"&gt;Mejorar el rendimiento con funciones as&amp;iacute;ncronas para la ejecuci&amp;oacute;n de procesos en paralelo&lt;/a&gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;2.&amp;nbsp;&lt;a style="color: #993300;" href="https://www.sergigisbert.com/blog/utilizar-concurrentbag-para-almacenar-el-resultado-de-metodos-asincronos/" target="_blank" rel="noopener"&gt;Utilizar ConcurrentBag para almacenar el resultado de m&amp;eacute;todos as&amp;iacute;ncronos&lt;/a&gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;3. Programaci&amp;oacute;n paralela en C# con la clase Parallel (Este art&amp;iacute;culo)&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Siguiendo con la serie de posts dedicados a la programaci&amp;oacute;n as&amp;iacute;ncrona y a la mejora de rendimiento utilizando&amp;nbsp;&lt;em&gt;Task&lt;/em&gt;&amp;nbsp;para la paralelizaci&amp;oacute;n de procesos, en este caso introducimos una nueva posibilidad utilizando la clase&amp;nbsp;&lt;em&gt;Parallel&lt;/em&gt;&amp;nbsp;que nos ofrece .Net, y la comparamos con las ejecuciones anteriores, donde recordamos que se ejecutaba un proceso que nos devolv&amp;iacute;a una lista de 10 n&amp;uacute;meros aleatorios entre 1 y 100, y lo dejamos en unos &lt;strong&gt;0,6s&lt;/strong&gt; de tiempo de ejecuci&amp;oacute;n, frente a los &lt;strong&gt;2s&lt;/strong&gt; originales de un proceso s&amp;iacute;ncrono:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;Result: 82,83,88,99,57,71,13,54,18,40
Completed: 00:00:00.6276547&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;El c&amp;oacute;digo fuente de ejemplo mostrado lo pod&amp;eacute;is encontrar en Github: &lt;a href="https://github.com/sgisbert/parallelization" target="_blank" rel="noopener"&gt;https://github.com/sgisbert/parallelization&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;En estos ejemplos, us&amp;aacute;bamos &lt;em&gt;Task.Run()&lt;/em&gt; para ejecutar el proceso en un nuevo hilo. Es importante entender que el uso de &lt;em&gt;Task&lt;/em&gt; para crear nuevos hilos de ejecuci&amp;oacute;n est&amp;aacute; limitado por el n&amp;uacute;mero de cores que tengamos en el procesador, y el uso excesivo de estos puede llevar a un uso poco eficiente de los recursos del sistema. Para entender m&amp;aacute;s estos conceptos, recomiendo leer este art&amp;iacute;culo:&amp;nbsp;&lt;a href="https://www.pluralsight.com/guides/using-task-run-async-await#module-knowthenatureofyourasynchronousoperation" target="_blank" rel="noopener"&gt;https://www.pluralsight.com/guides/using-task-run-async-await#module-knowthenatureofyourasynchronousoperation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;El uso de &lt;em&gt;async/await&lt;/em&gt; es especialmente recomendable junto con librer&amp;iacute;as o APIs que ofrecen m&amp;eacute;todos as&amp;iacute;ncronos sin necesidad de acudir a &lt;em&gt;Task.Run()&lt;/em&gt; para crearlos. Buenos ejemplos de este caso son&amp;nbsp;&lt;em&gt;HttpClient&lt;/em&gt; o&amp;nbsp;&lt;em&gt;FileStream&lt;/em&gt;, que ofrecen m&amp;eacute;todos de I/O as&amp;iacute;ncronos donde se pueden aprovechar las virtudes de la programaci&amp;oacute;n as&amp;iacute;ncrona.&lt;/p&gt;
&lt;p&gt;Si a&amp;uacute;n as&amp;iacute; tenemos un caso como el del ejemplo, donde tenemos que realizar un mismo procesamiento sobre m&amp;uacute;ltiples instancias diferentes, podemos aprovechar la clase&amp;nbsp;&lt;em&gt;Parallel&lt;/em&gt;, y ver c&amp;oacute;mo se comporta en comparaci&amp;oacute;n con &lt;em&gt;Task.WaitAll()&lt;/em&gt; o&amp;nbsp;&lt;em&gt;Task.WhenAll().&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Task.WaitAll() vs Task.WhenAll()&lt;/h2&gt;
&lt;p&gt;Existen dos maneras de esperar a que todas nuestras tareas terminen de ejecutarse:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;await Task.WhenAll(tasks.ToArray());&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y&amp;nbsp;&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;Task.WaitAll(tasks.ToArray());&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y es interesante conocer la diferencia entre las dos. En t&amp;eacute;rminos de rendimiento, en ambos casos el proceso dura un tiempo similar: 0,6s, pero hay ciertos matices interesantes a conocer:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Task.WaitAll()&lt;/em&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bloquea el thread principal de ejecuci&amp;oacute;n hasta que todas las tareas terminen, por lo que no devuelve una &lt;em&gt;Task&lt;/em&gt;. Esto significa que si tenemos una aplicaci&amp;oacute;n con UI, &amp;eacute;ste quedar&amp;aacute; bloqueado hasta que termine la ejecuci&amp;oacute;n&lt;/li&gt;
&lt;li&gt;Si se producen excepciones en una o varias tareas, lanzar&amp;aacute; una&amp;nbsp;&lt;em&gt;AggregateException&amp;nbsp;&lt;/em&gt;con todas las excepciones que se hayan producido&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;await Task.WhenAll()&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No bloquea el thread principal, por lo que ser&amp;iacute;a el m&amp;eacute;todo recomendado para aplicaci&amp;oacute;n con UI y m&amp;eacute;todos async, ya que s&amp;iacute; devuelve una&amp;nbsp;&lt;em&gt;Task&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Si se producen excepciones, s&amp;oacute;lo lanzar&amp;aacute; la primera que ocurra.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En este art&amp;iacute;culo se detalla con m&amp;aacute;s nivel de detalle las diferencias entre ambos m&amp;eacute;todos:&amp;nbsp;&lt;a href="https://www.infoworld.com/article/3152735/when-to-use-taskwaitall-vs-taskwhenall-in-net.html" target="_blank" rel="noopener"&gt;https://www.infoworld.com/article/3152735/when-to-use-taskwaitall-vs-taskwhenall-in-net.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Utilizando la clase Parallel&lt;/h2&gt;
&lt;p&gt;Otra manera de aprovechar las ventajas de la computaci&amp;oacute;n paralela, es utilizar la clase&amp;nbsp;&lt;em&gt;Parallel&lt;/em&gt; de .Net (m&amp;aacute;s info en la &lt;a href="https://docs.microsoft.com/dotnet/api/system.threading.tasks.parallel?view=net-5.0" target="_blank" rel="noopener"&gt;documentaci&amp;oacute;n oficial&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Lo primero que haremos con nuestro c&amp;oacute;digo de ejemplo ser&amp;aacute; transformar nuestro proceso para que no necesite utilizar&amp;nbsp;&lt;em&gt;Task.Run()&lt;/em&gt;, pero manteniendo la compatibilidad con&amp;nbsp;&lt;em&gt;Task&lt;/em&gt; para comparar ambos resultados. Para ello, modificamos el m&amp;eacute;todo process de la siguiente manera (&lt;a href="https://github.com/sgisbert/parallelization/blob/540ac5c9138c8549ee1ce87efb74948424309abc/src/Program.cs" target="_blank" rel="noopener"&gt;ver archivo completo en GitHub&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;private static async Task Process(int id, ConcurrentBag&amp;lt;int&amp;gt; cb)
{
    await Task.Run(() =&amp;gt;
    {
        CoreProcess(id, cb);
    });
}

private static void CoreProcess(int id, ConcurrentBag&amp;lt;int&amp;gt; cb)
{
    Stopwatch timer = new Stopwatch();
    timer.Start();
    Random random = new Random();
    Thread.Sleep(200);

    int number = random.Next(1, 100);
    cb.Add(number);

    Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] Process {id}: {timer.Elapsed}");
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hemos a&amp;ntilde;adido tambi&amp;eacute;n el valor de&amp;nbsp;&lt;em&gt;Thread.CurrentThread.ManagedThreadId&lt;/em&gt; a la salida por consola para comprobar los Threads que se crean.&lt;/p&gt;
&lt;p&gt;De esta manera, podemos llamar a nuestro proceso tanto de manera as&amp;iacute;ncrona como s&amp;iacute;ncrona.&lt;/p&gt;
&lt;h3&gt;Parallel.ForEach()&lt;/h3&gt;
&lt;p&gt;A&amp;ntilde;adimos el siguiente c&amp;oacute;digo, que utiliza&amp;nbsp;&lt;a href="https://docs.microsoft.com/dotnet/api/system.threading.tasks.parallel.foreach?view=net-5.0" target="_blank" rel="noopener"&gt;&lt;em&gt;Parallel.ForEach()&lt;/em&gt;&lt;/a&gt; para lanzar el proceso s&amp;iacute;ncrono, pero usando paralelismo (&lt;a href="https://github.com/sgisbert/parallelization/blob/04c243d2b4b50d69315fbd562bd2739f95627374/src/Program.cs" target="_blank" rel="noopener"&gt;ver archivo completo en GitHub&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;timer.Restart();
ConcurrentBag&amp;lt;int&amp;gt; cb2 = new ConcurrentBag&amp;lt;int&amp;gt;();
var sourceCollection = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Parallel.ForEach(sourceCollection, (id) =&amp;gt;
{
    CoreProcess(id, cb2);
});
Console.WriteLine($"Result: {string.Join(",", cb2)}");
Console.WriteLine($"Completed: {timer.Elapsed}");
Console.WriteLine();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y comparamos los resultados:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Task.WaitAll()&lt;/em&gt;&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;[6] Process 2: 00:00:00.2028313
[5] Process 1: 00:00:00.2039001
[4] Process 0: 00:00:00.2063105
[7] Process 3: 00:00:00.2027921
[4] Process 5: 00:00:00.2006033
[7] Process 7: 00:00:00.2006397
[5] Process 6: 00:00:00.2006732
[6] Process 4: 00:00:00.2007073
[7] Process 8: 00:00:00.2001104
[5] Process 9: 00:00:00.2009985

Result: 15,49,52,76,90,1,71,67,37,23
Completed: 00:00:00.6398704
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Parallel.ForEach()&lt;/em&gt;&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;[6] Process 9: 00:00:00.2011420
[5] Process 3: 00:00:00.2012138
[1] Process 1: 00:00:00.2013376
[4] Process 7: 00:00:00.2011324
[7] Process 5: 00:00:00.2011974
[1] Process 2: 00:00:00.2004285
[4] Process 8: 00:00:00.2004063
[7] Process 6: 00:00:00.2005255
[6] Process 10: 00:00:00.2006825
[5] Process 4: 00:00:00.2006454
Result: 50,82,38,39,37,96,80,48,95,90
Completed: 00:00:00.4507518&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La tarea ejecutada con&amp;nbsp;&lt;em&gt;Parallel&lt;/em&gt; ha bajado el tiempo a &lt;strong&gt;0,45s&lt;/strong&gt; frente a los &lt;strong&gt;0,6s&lt;/strong&gt; de&amp;nbsp;&lt;em&gt;Task&lt;/em&gt;. Hemos obtenido una mejora de rendimiento sin tener que ejecutar&amp;nbsp;&lt;em&gt;Task.Run()&lt;/em&gt; que puede ocasionar problemas con el uso de los recursos disponibles.&lt;/p&gt;
&lt;h3&gt;Parallel.Invoke()&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Parallel&lt;/em&gt; nos ofrece otra alternativa adicional para invocar tareas en paralelo, con el m&amp;eacute;todo&amp;nbsp;&lt;em&gt;&lt;a href="https://docs.microsoft.com/dotnet/api/system.threading.tasks.parallel.invoke?view=net-5.0" target="_blank" rel="noopener"&gt;Invoke()&lt;/a&gt;&lt;/em&gt;. Para probar su uso, a&amp;ntilde;adimos las siguientes l&amp;iacute;neas de c&amp;oacute;digo (&lt;a href="https://github.com/sgisbert/parallelization/blob/6019085440710a0345fe2ed2f4bc3c3a1f414619/src/Program.cs" target="_blank" rel="noopener"&gt;ver archivo completo en Github&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;timer.Restart();
ConcurrentBag&amp;lt;int&amp;gt; cb3 = new ConcurrentBag&amp;lt;int&amp;gt;();
List&amp;lt;Action&amp;gt; actions = new List&amp;lt;Action&amp;gt;();
foreach (var i in sourceCollection)
{
    actions.Add(() =&amp;gt; CoreProcess(i, cb3));
}
Parallel.Invoke(actions.ToArray());
Console.WriteLine($"Result: {string.Join(",", cb3)}");
Console.WriteLine($"Completed: {timer.Elapsed}");&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Con los siguientes resultados:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;[4] Process 3: 00:00:00.2008078
[6] Process 4: 00:00:00.2008435
[5] Process 5: 00:00:00.2007954
[7] Process 2: 00:00:00.2008161
[1] Process 1: 00:00:00.2009441
[7] Process 8: 00:00:00.2001254
[5] Process 6: 00:00:00.2001896
[4] Process 7: 00:00:00.2001298
[8] Process 10: 00:00:00.2033235
[6] Process 9: 00:00:00.2089627
Result: 55,26,74,29,32,45,71,46,39,90
Completed: 00:00:00.4121676&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En este caso,&amp;nbsp;&lt;em&gt;Invoke()&lt;/em&gt; reduce el tiempo hasta casi &lt;strong&gt;0,4s&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Conclusiones&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Analiza bien cada situaci&amp;oacute;n, entendiendo c&amp;oacute;mo funcionan los m&amp;eacute;todos as&amp;iacute;ncronos y las diferencias entre&amp;nbsp;&lt;em&gt;Task.WaitAll&lt;/em&gt; y&amp;nbsp;&lt;em&gt;Task.WhenAll&lt;/em&gt;, para utilizarlos correctamente en cada caso&lt;/li&gt;
&lt;li&gt;Conoce otras alternativas como&amp;nbsp;&lt;em&gt;Parallel&lt;/em&gt; para mejorar el rendimiento de tus aplicaciones&lt;/li&gt;
&lt;li&gt;No te creas los resultados de estos ejemplos. Haz pruebas de rendimiento por ti mismo y comprueba en cada caso qu&amp;eacute; te funciona mejor&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://stage.sergigisbert.com/blog/make-use-of-concurrentbag-to-store-the-results-from-asynchronous-processes/</id>
    <title>Make use of ConcurrentBag to store the results from asynchronous processes</title>
    <updated>2021-02-13T16:29:50Z</updated>
    <published>2021-01-30T11:46:00Z</published>
    <link href="https://stage.sergigisbert.com/blog/make-use-of-concurrentbag-to-store-the-results-from-asynchronous-processes/" />
    <author>
      <name>test@example.com</name>
      <email>Sergi Gisbert</email>
    </author>
    <category term="c#" />
    <category term="dotnet core" />
    <category term="performance" />
    <category term="tips" />
    <category term="async/await" />
    <category term="concurrentbag" />
    <category term="english" />
    <content type="html">&lt;p&gt;&lt;span style="color: #808080;"&gt;&lt;em&gt;Disclaimer: the methods and code examples in this article are the fruit of my own investigation and self learning, and in no means these are ready to be used in a real, production environment. Use at your own risk. To learn more about parallel and synchronous programming, I recommend reading the official documentation at: &lt;a href="https://docs.microsoft.com/dotnet/standard/parallel-processing-and-concurrency" target="_blank" rel="noopener"&gt;https://docs.microsoft.com/dotnet/standard/parallel-processing-and-concurrency&lt;/a&gt;&amp;nbsp;&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;This post is part of a series of 3 articles about performace improvement with parallel programming:&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;1. &lt;a style="color: #993300;" href="https://www.sergigisbert.com/blog/improve-the-performance-with-asynchronous-functions-to-run-processes-in-parallel/" target="_blank" rel="noopener"&gt;Improve the performance with asynchronous functions to run processes in parallel&lt;/a&gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;2. Make use of ConcurrentBag to store the results from asynchronous processes&amp;nbsp;(this article)&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;3. &lt;a style="color: #993300;" href="https://www.sergigisbert.com/blog/parallel-programming-in-c-with-class-parallel/" target="_blank" rel="noopener"&gt;Parallel programming in C# with class Parallel&lt;/a&gt;&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;From my&amp;nbsp;&lt;a href="https://www.sergigisbert.com/blog/mejorar-el-rendimiento-con-funciones-asincronas-para-la-ejecucion-de-procesos-en-paralelo/"&gt;previous post&lt;/a&gt;&amp;nbsp;about parallelizing process execution, I am extending the sample there to show how we can get that different processes in parallel can combine their results into the same object, without the asynchronous execution being a problem. In this case, we are going to get all the results from the execution in the same data list.&lt;/p&gt;
&lt;p&gt;Source code in this article can be found on Github:&amp;nbsp;&lt;a href="https://github.com/sgisbert/parallelization" target="_blank" rel="noopener"&gt;https://github.com/sgisbert/parallelization&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Starting point&lt;/h2&gt;
&lt;p&gt;In the &lt;a href="https://www.sergigisbert.com/blog/mejorar-el-rendimiento-con-funciones-asincronas-para-la-ejecucion-de-procesos-en-paralelo/"&gt;previous post&lt;/a&gt;&amp;nbsp;we managed to reduce the processing time from 2 seconds down to 0.6 seconds, althoug actually our method was not returning any result. For this sample, I am modifying it to make it return a random number between 1 and 100, while we keep the delay to show the execution time.&lt;/p&gt;
&lt;p&gt;Let's modify the &lt;em&gt;Process()&lt;/em&gt;&amp;nbsp;method to return an integer (&lt;a href="https://github.com/sgisbert/parallelization/blob/c446abc728e858661d99be690e3c43833f11b6d3/src/Program.cs"&gt;see full file on Github&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;private static async Task&amp;lt;int&amp;gt; Process(int id)
{
    var number = await Task&amp;lt;int&amp;gt;.Run(() =&amp;gt;
    {
        Stopwatch timer = new Stopwatch();
        timer.Start();
        Random random = new Random();
        Thread.Sleep(200);

        int number = random.Next(1,10);

        Console.WriteLine($"Process {id}: {timer.Elapsed}");
        return number;
    });
    return number;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And add the necessary code to get the results in the main thread:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;List&amp;lt;Task&amp;lt;int&amp;gt;&amp;gt; tasks = new List&amp;lt;Task&amp;lt;int&amp;gt;&amp;gt;();
for (int i = 0; i &amp;lt; 10; i++)
{
    tasks.Add(Process(i));
}
Task.WaitAll(tasks.ToArray());

List&amp;lt;int&amp;gt; results = new List&amp;lt;int&amp;gt;();
foreach (var task in tasks)
{
    results.Add(task.Result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that changing the return type for &lt;em&gt;Process()&lt;/em&gt;&amp;nbsp;to&amp;nbsp;&lt;em&gt;Task&lt;strong&gt;&amp;lt;int&amp;gt;&lt;/strong&gt;&lt;/em&gt;, we also need to change it for the list where we are storing the tasks that are being executed to as well to&amp;nbsp;&lt;em&gt;List&amp;lt;Task&lt;strong&gt;&amp;lt;int&amp;gt;&lt;/strong&gt;&amp;gt;&lt;/em&gt;, so we can access later to the result with&amp;nbsp;&lt;em&gt;task.&lt;strong&gt;Result&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The execution result is as follows:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;Process 0: 00:00:00.2077483
Process 1: 00:00:00.2076874
Process 3: 00:00:00.2074275
Process 2: 00:00:00.2057897
Process 7: 00:00:00.2097799
Process 6: 00:00:00.2101881
Process 5: 00:00:00.2103866
Process 4: 00:00:00.2109039
Process 9: 00:00:00.2004370
Process 8: 00:00:00.2006408

Result: 76,2,79,86,12,45,27,17,55,5
Completed: 00:00:00.6971794
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this sample, we are processing all the results after all the tasks have finalized the execution. For this simple example, the second&amp;nbsp;&lt;em&gt;foreach&lt;/em&gt; loop has increased the execution time in almost one tenth, getting closer to 0.7s from the originally 0.6s. Imagine a more complex case where you should analyze hundreds or thousands results, it is possible you would lose most of the performance gains you got from parallelizing the process.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Getting the results already processed&lt;/h2&gt;
&lt;p&gt;So that we don't need to post process the results from the asynchronous methods in a loop, we can make use of concurrent data types, which are thread-safe, like&amp;nbsp;&lt;em&gt;&lt;a href="https://docs.microsoft.com/dotnet/api/system.collections.concurrent.blockingcollection-1" target="_blank" rel="noopener"&gt;ConcurrentBag&amp;lt;T&amp;gt;&lt;/a&gt;&lt;/em&gt;. There are also concurrent versions for &lt;em&gt;Dictionary, Queue&lt;/em&gt;&amp;nbsp;and&amp;nbsp;&lt;em&gt;Stack&lt;/em&gt;. With these data types, each process will add its results directly to the same ConcurrentBag as the others, so once back to the main thread, there's no need to loop over the results again.&lt;/p&gt;
&lt;p&gt;To do so, let's add the ConcurrentBag as a parameter to our &lt;em&gt;Process()&lt;/em&gt;&amp;nbsp;method (&lt;a href="https://github.com/sgisbert/parallelization/blob/1c15b608eb976170bdca07a2b574a6d77b3de0e5/src/Program.cs"&gt;see full file on Github&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;private static async Task Process(int id, ConcurrentBag&amp;lt;int&amp;gt; cb)
{
    await Task.Run(() =&amp;gt;
    {
        Stopwatch timer = new Stopwatch();
        timer.Start();
        Random random = new Random();
        Thread.Sleep(200);

        int number = random.Next(1,100);
        cb.Add(number);

        Console.WriteLine($"Process {id}: {timer.Elapsed}");
    });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we don't need to return a &lt;em&gt;Task&amp;lt;int&amp;gt;&lt;/em&gt;, nor process again the results in the main thread, but we can make use of them directly from the ConcurrentBag:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;static async Task Main(string[] args)
{
    ConcurrentBag&amp;lt;int&amp;gt; cb = new ConcurrentBag&amp;lt;int&amp;gt;();
    Stopwatch timer = new Stopwatch();
    timer.Start();

    List&amp;lt;Task&amp;gt; tasks = new List&amp;lt;Task&amp;gt;();
    for (int i = 0; i &amp;lt; 10; i++)
    {
        tasks.Add(Process(i, cb));
    }
    Task.WaitAll(tasks.ToArray());

    Console.WriteLine();
    Console.WriteLine($"Result: {string.Join(",", cb)}");
    Console.WriteLine($"Completed: {timer.Elapsed}");
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Back to the results:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;Process 1: 00:00:00.2029103
Process 2: 00:00:00.2028785
Process 0: 00:00:00.2029188
Process 3: 00:00:00.2028485
Process 5: 00:00:00.2007397
Process 7: 00:00:00.2007120
Process 6: 00:00:00.2007211
Process 4: 00:00:00.2008456
Process 9: 00:00:00.2001557
Process 8: 00:00:00.2007588

Result: 82,83,88,99,57,71,13,54,18,40
Completed: 00:00:00.6276547&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we are back again to be closer to the 0.6 seconds than before, once we removed the unnecessary loop to process the results.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;It is possible to execute processes in parallel and get the results grouped in the same object. This will avoid the need to post-process the results, which would add an unnecessary execution time.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;There are thread-safe data types for collections that can be used to store the results from asynchronous tasks, like &lt;em&gt;ConcurrentBag&lt;/em&gt;,&amp;nbsp;&lt;em&gt;ConcurrentDictionary&lt;/em&gt;,&amp;nbsp;&lt;em&gt;ConcurrentQueue&lt;/em&gt;&amp;nbsp;or&amp;nbsp;&lt;em&gt;ConcurrentStack&lt;/em&gt;. Learning how to use them will give us better options to improve our applications.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://stage.sergigisbert.com/blog/utilizar-concurrentbag-para-almacenar-el-resultado-de-metodos-asincronos/</id>
    <title>Utilizar ConcurrentBag para almacenar el resultado de métodos asíncronos</title>
    <updated>2021-02-13T16:11:11Z</updated>
    <published>2021-01-30T11:44:00Z</published>
    <link href="https://stage.sergigisbert.com/blog/utilizar-concurrentbag-para-almacenar-el-resultado-de-metodos-asincronos/" />
    <author>
      <name>test@example.com</name>
      <email>Sergi Gisbert</email>
    </author>
    <category term="c#" />
    <category term="dotnet core" />
    <category term="performance" />
    <category term="tips" />
    <category term="async/await" />
    <category term="concurrentbag" />
    <content type="html">&lt;p&gt;&lt;span style="color: #808080;"&gt;&lt;em&gt;Disclaimer: los m&amp;eacute;todos y ejemplos de c&amp;oacute;digo de este art&amp;iacute;culo son fruto de mi propia investigaci&amp;oacute;n y aprendizaje aut&amp;oacute;nomo, y en ning&amp;uacute;n caso est&amp;aacute;n preparados para su utilizaci&amp;oacute;n en c&amp;oacute;digo real de producci&amp;oacute;n. El autor no se hace responsable del uso de estos ejemplos. Para m&amp;aacute;s informaci&amp;oacute;n sobre programaci&amp;oacute;n paralela y as&amp;iacute;ncrona y sus connotaciones, recomiendo acceder a la documentaci&amp;oacute;n oficial: &lt;a style="color: #808080;" href="https://docs.microsoft.com/es-es/dotnet/standard/parallel-processing-and-concurrency" target="_blank" rel="noopener"&gt;https://docs.microsoft.com/es-es/dotnet/standard/parallel-processing-and-concurrency&lt;/a&gt;&amp;nbsp;&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;Este post forma parte de una serie de 3 art&amp;iacute;culos sobre mejoras de rendimiento utilizando programaci&amp;oacute;n paralela:&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;1. &lt;a style="color: #993300;" href="https://www.sergigisbert.com/blog/mejorar-el-rendimiento-con-funciones-asincronas-para-la-ejecucion-de-procesos-en-paralelo/" target="_blank" rel="noopener"&gt;Mejorar el rendimiento con funciones as&amp;iacute;ncronas para la ejecuci&amp;oacute;n de procesos en paralelo&lt;/a&gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;2. Utilizar ConcurrentBag para almacenar el resultado de m&amp;eacute;todos as&amp;iacute;ncronos&lt;/em&gt;&lt;em&gt;&amp;nbsp;(Este art&amp;iacute;culo)&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;3. &lt;a style="color: #993300;" href="https://www.sergigisbert.com/blog/programacion-paralela-en-c-con-la-clase-parallel/" target="_blank" rel="noopener"&gt;Programaci&amp;oacute;n paralela en C# con la clase Parallel&lt;/a&gt;&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Al hilo del &lt;a href="https://www.sergigisbert.com/blog/mejorar-el-rendimiento-con-funciones-asincronas-para-la-ejecucion-de-procesos-en-paralelo/"&gt;post anterior&lt;/a&gt; sobre ejecuci&amp;oacute;n paralela de tareas, voy a ampliar el ejemplo para demostrar como podemos conseguir que diferentes procesos en paralelo puedan combinar el resultado en un mismo objeto, sin que la ejecuci&amp;oacute;n as&amp;iacute;ncrona sea un problema. En este caso, vamos a obtener los resultados del procesamiento en una misma lista de datos.&lt;/p&gt;
&lt;p&gt;El c&amp;oacute;digo fuente de ejemplo mostrado lo pod&amp;eacute;is encontrar en Github:&amp;nbsp;&lt;a href="https://github.com/sgisbert/parallelization" target="_blank" rel="noopener"&gt;https://github.com/sgisbert/parallelization&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Punto de partida&lt;/h2&gt;
&lt;p&gt;En el&amp;nbsp;&lt;a href="https://www.sergigisbert.com/blog/mejorar-el-rendimiento-con-funciones-asincronas-para-la-ejecucion-de-procesos-en-paralelo/"&gt;post anterior&lt;/a&gt; conseguimos reducir el tiempo de procesamiento de 2 segundos a 0,6 segundos, aunque en realidad nuestra funci&amp;oacute;n no devolv&amp;iacute;a ning&amp;uacute;n resultado. Para este ejemplo, vamos a modificarla para que devuelva un n&amp;uacute;mero aleatorio entre 1 y 100, mientras mantenemos el retardo para ilustrar el tiempo de ejecuci&amp;oacute;n.&lt;/p&gt;
&lt;p&gt;Modificamos el m&amp;eacute;todo&amp;nbsp;&lt;em&gt;Process()&lt;/em&gt; para devolver un entero (&lt;a href="https://github.com/sgisbert/parallelization/blob/c446abc728e858661d99be690e3c43833f11b6d3/src/Program.cs"&gt;ver archivo completo en Github&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;private static async Task&amp;lt;int&amp;gt; Process(int id)
{
    var number = await Task&amp;lt;int&amp;gt;.Run(() =&amp;gt;
    {
        Stopwatch timer = new Stopwatch();
        timer.Start();
        Random random = new Random();
        Thread.Sleep(200);

        int number = random.Next(1,10);

        Console.WriteLine($"Process {id}: {timer.Elapsed}");
        return number;
    });
    return number;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y a&amp;ntilde;adimos el c&amp;oacute;digo necesario para recoger los resultados en el hilo principal:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;List&amp;lt;Task&amp;lt;int&amp;gt;&amp;gt; tasks = new List&amp;lt;Task&amp;lt;int&amp;gt;&amp;gt;();
for (int i = 0; i &amp;lt; 10; i++)
{
    tasks.Add(Process(i));
}
Task.WaitAll(tasks.ToArray());

List&amp;lt;int&amp;gt; results = new List&amp;lt;int&amp;gt;();
foreach (var task in tasks)
{
    results.Add(task.Result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notar c&amp;oacute;mo al cambiar el tipo devuelto por&amp;nbsp;&lt;em&gt;Process()&lt;/em&gt; a&amp;nbsp;&lt;em&gt;Task&lt;strong&gt;&amp;lt;int&amp;gt;&lt;/strong&gt;&lt;/em&gt;, tambi&amp;eacute;n tenemos que cambiar el tipo de datos de la lista donde guardamos las tareas que se van ejecutando a &lt;em&gt;List&amp;lt;Task&lt;strong&gt;&amp;lt;int&amp;gt;&lt;/strong&gt;&amp;gt;&lt;/em&gt;, para poder tener acceso al resultado posteriomente mediante&amp;nbsp;&lt;em&gt;task.&lt;strong&gt;Result&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;El resultado de la ejecuci&amp;oacute;n es el siguiente:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;Process 0: 00:00:00.2077483
Process 1: 00:00:00.2076874
Process 3: 00:00:00.2074275
Process 2: 00:00:00.2057897
Process 7: 00:00:00.2097799
Process 6: 00:00:00.2101881
Process 5: 00:00:00.2103866
Process 4: 00:00:00.2109039
Process 9: 00:00:00.2004370
Process 8: 00:00:00.2006408

Result: 76,2,79,86,12,45,27,17,55,5
Completed: 00:00:00.6971794
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En este ejemplo, hemos tenido que procesar todos los resultados a posteriori, una vez todos los procesos han finalizado la ejecuci&amp;oacute;n. Para este ejemplo sencillo, este segundo bucle&amp;nbsp;&lt;em&gt;foreach&lt;/em&gt; ha incrementado el tiempo en casi una d&amp;eacute;cima, pasando de los 0,6s originales a casi 0,7s. Imaginemos un caso complejo, donde hubiera que analizar cientos o miles de resultados, y es posible que estuvi&amp;eacute;ramos perdiendo gran parte del rendimiento conseguido con la paralelizaci&amp;oacute;n del proceso.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Devolver el resultado ya procesado&lt;/h2&gt;
&lt;p&gt;Para no tener que recuperar los resultados de los procesos as&amp;iacute;ncronos en un bucle posterior, podemos hacer uso listas concurrentes que son thread-safe, como&amp;nbsp;&lt;em&gt;&lt;a href="https://docs.microsoft.com/es-es/dotnet/api/system.collections.concurrent.concurrentbag-1" target="_blank" rel="noopener"&gt;ConcurrentBag&amp;lt;T&amp;gt;&lt;/a&gt;&lt;/em&gt;. Tambi&amp;eacute;n existen versiones concurrentes de&amp;nbsp;&lt;em&gt;Dictionary, Queue&lt;/em&gt; y &lt;em&gt;Stack&lt;/em&gt;. Con este tipo de datos, cada proceso podr&amp;aacute; a&amp;ntilde;adir su resultado directamente a la ConcurrentBag, de manera que al volver al hilo principal, ya no ser&amp;aacute; necesario recorrer todas las tareas para obtener los resultados.&lt;/p&gt;
&lt;p&gt;Para ello, a&amp;ntilde;adimos la ConcurrentBag como par&amp;aacute;metro a nuestro m&amp;eacute;todo&amp;nbsp;&lt;em&gt;Process()&lt;/em&gt; (&lt;a href="https://github.com/sgisbert/parallelization/blob/1c15b608eb976170bdca07a2b574a6d77b3de0e5/src/Program.cs"&gt;ver archivo completo en Github&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;private static async Task Process(int id, ConcurrentBag&amp;lt;int&amp;gt; cb)
{
    await Task.Run(() =&amp;gt;
    {
        Stopwatch timer = new Stopwatch();
        timer.Start();
        Random random = new Random();
        Thread.Sleep(200);

        int number = random.Next(1,100);
        cb.Add(number);

        Console.WriteLine($"Process {id}: {timer.Elapsed}");
    });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Con lo que ya no es necesario devolver un&amp;nbsp;&lt;em&gt;Task&amp;lt;int&amp;gt;&lt;/em&gt;, ni volver a procesar los resultados en el hilo principal, sino que podemos hacer uso directamente de los resultados que tenemos en la ConcurrentBag:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;static async Task Main(string[] args)
{
    ConcurrentBag&amp;lt;int&amp;gt; cb = new ConcurrentBag&amp;lt;int&amp;gt;();
    Stopwatch timer = new Stopwatch();
    timer.Start();

    List&amp;lt;Task&amp;gt; tasks = new List&amp;lt;Task&amp;gt;();
    for (int i = 0; i &amp;lt; 10; i++)
    {
        tasks.Add(Process(i, cb));
    }
    Task.WaitAll(tasks.ToArray());

    Console.WriteLine();
    Console.WriteLine($"Result: {string.Join(",", cb)}");
    Console.WriteLine($"Completed: {timer.Elapsed}");
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Volviendo a los resultados:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;Process 1: 00:00:00.2029103
Process 2: 00:00:00.2028785
Process 0: 00:00:00.2029188
Process 3: 00:00:00.2028485
Process 5: 00:00:00.2007397
Process 7: 00:00:00.2007120
Process 6: 00:00:00.2007211
Process 4: 00:00:00.2008456
Process 9: 00:00:00.2001557
Process 8: 00:00:00.2007588

Result: 82,83,88,99,57,71,13,54,18,40
Completed: 00:00:00.6276547&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;de nuevo comprobamos que estamos m&amp;aacute;s cerca de los 0,6 segundos que antes, al eliminar el post procesado innecesario de los resultados.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Conclusiones&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Es posible ejecutar procesos en paralelo y obtener los resultados agrupados en un mismo objeto. Esto nos evitar&amp;aacute; un post procesado posterios de las tareas que a&amp;ntilde;ade tiempo innecesario de ejecuci&amp;oacute;n.&lt;/li&gt;
&lt;li&gt;Existen tipos de datos thread-safe que podemos utilizar para almacenar resultados de tareas as&amp;iacute;ncronas, como &lt;em&gt;ConcurrentBag&lt;/em&gt;,&amp;nbsp;&lt;em&gt;ConcurrentDictionary&lt;/em&gt;,&amp;nbsp;&lt;em&gt;ConcurrentQueue&lt;/em&gt; o&amp;nbsp;&lt;em&gt;ConcurrentStack&lt;/em&gt;. Aprender a usarlos nos abre nuevas opciones de mejora de nuestros procesos.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://stage.sergigisbert.com/blog/improve-the-performance-with-asynchronous-functions-to-run-processes-in-parallel/</id>
    <title>Improve the performance with asynchronous functions to run processes in parallel</title>
    <updated>2021-02-13T16:28:24Z</updated>
    <published>2021-01-30T10:30:24Z</published>
    <link href="https://stage.sergigisbert.com/blog/improve-the-performance-with-asynchronous-functions-to-run-processes-in-parallel/" />
    <author>
      <name>test@example.com</name>
      <email>Sergi Gisbert</email>
    </author>
    <category term="c#" />
    <category term="dotnet core" />
    <category term="performance" />
    <category term="tips" />
    <category term="async/await" />
    <category term="english" />
    <content type="html">&lt;p&gt;&lt;span style="color: #808080;"&gt;&lt;em&gt;Disclaimer: the methods and code examples in this article are the fruit of my own investigation and self learning, and in no means these are ready to be used in a real, production environment. Use at your own risk. To learn more about parallel and synchronous programming, I recommend reading the official documentation at: &lt;a style="color: #808080;" href="https://docs.microsoft.com/dotnet/standard/parallel-processing-and-concurrency%20" target="_blank" rel="noopener"&gt;https://docs.microsoft.com/dotnet/standard/parallel-processing-and-concurrency&amp;nbsp;&lt;/a&gt;&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;This post is part of a series of 3 articles about performace improvement with parallel programming:&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;1. Improve the performance with asynchronous functions to run processes in parallel (this article)&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;2. &lt;a style="color: #993300;" href="https://www.sergigisbert.com/blog/make-use-of-concurrentbag-to-store-the-results-from-asynchronous-processes/" target="_blank" rel="noopener"&gt;Make use of ConcurrentBag to store the results from asynchronous processes&lt;/a&gt;&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #993300;"&gt;&lt;em&gt;3. &lt;a style="color: #993300;" href="https://www.sergigisbert.com/blog/parallel-programming-in-c-with-class-parallel/" target="_blank" rel="noopener"&gt;Parallel programming in C# with class Parallel&lt;/a&gt;&amp;nbsp;&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Recently I was collaborating with a colleague at work because the running time of a process was far slower than expected. Reviewing the code, we identified that parts of the process could be done in parallel, with which the overall performance was improved by a 200%. In this post, I'll show some basic techniques to work with processes parallelization and will display the performance improvements that can be achieved.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Starting point&lt;/h2&gt;
&lt;p&gt;I am using a simple Console application in C# to showcase an elementary case, to see how we can improve the performance using parallel computing techiques. Sample source code can be found here:&amp;nbsp;&lt;a href="https://github.com/sgisbert/parallelization" target="_blank" rel="noopener"&gt;https://github.com/sgisbert/parallelization&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The starting code is as follows (&lt;a href="https://github.com/sgisbert/parallelization/blob/4c3b57b019822c9bd6000bc760511276c9b25351/src/Program.cs" target="_blank" rel="noopener"&gt;Ver en Github&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;using System.Threading;
using System.Diagnostics;
using System;

namespace parallel
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch timer = new Stopwatch();
            timer.Start();

            for (int i = 0; i &amp;lt; 10; i++)
            {
                Process(i);
            }

            Console.WriteLine($"Completed: {timer.Elapsed}");
        }

        private static void Process(int id)
        {
            Stopwatch timer = new Stopwatch();
            timer.Start();
            Thread.Sleep(200);
            Console.WriteLine($"Process {id}: {timer.Elapsed}");
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have a process that takes 200ms to complete, and we call it 10 times in a row with a&amp;nbsp;&lt;em&gt;for&lt;/em&gt; loop. As expected, this code execution takes 2 seconds to complete, as each process has to wait for the previous one to finish before it is started:&amp;nbsp;&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;Process 0: 00:00:00.2018134
Process 1: 00:00:00.2003564
Process 2: 00:00:00.2003647
Process 3: 00:00:00.2007673
Process 4: 00:00:00.2008702
Process 5: 00:00:00.2004851
Process 6: 00:00:00.2003682
Process 7: 00:00:00.2009726
Process 8: 00:00:00.2009657
Process 9: 00:00:00.2006482
Completed: 00:00:02.0362772&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Converting the method into asynchronous&lt;/h2&gt;
&lt;p&gt;The goal is that these 10 processes could be executed asynchronously and parallelized, so they don't need to wait for the others to start. First step would be converting the &lt;em&gt;Process()&lt;/em&gt;&amp;nbsp;method into an asynchronous function, so we are doing the following changes (&lt;a href="https://github.com/sgisbert/parallelization/blob/fdb32905ee5347c37052bc7c3cc129a990dcf8fb/src/Program.cs" target="_blank" rel="noopener"&gt;see full file on Github&lt;/a&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change method signature:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;private static async Task Process(int id)&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Change the call:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;await Process(i);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Change the &lt;em&gt;Main&lt;/em&gt; method signature, so it is&amp;nbsp;asynchronous as well:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;static async Task Main(string[] args)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With these changes, we have our asynchronous method, but, &amp;iquest;is the performance better now? If we run the application again, we are getting exactly the same results as at the beginning, this is, we still have the same problem.&lt;/p&gt;
&lt;p&gt;This is due to the use of the&amp;nbsp;&lt;em&gt;await&lt;/em&gt;&amp;nbsp;keyword, with which we are literally saying "wait until it finishes to continue". This is, we are executing an asynchronous method in a synchronous way, and this is not what we want.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Executing the process asynchronously&lt;/h2&gt;
&lt;p&gt;To run the process in an asynchronous way, we need to call the function without the &lt;em&gt;await&lt;/em&gt; keyword. In this case, the method will return a &lt;em&gt;Task&lt;/em&gt;&amp;nbsp;object, instead of the function return value&amp;nbsp;(althoug it was &lt;em&gt;void&lt;/em&gt; in this sample, it could be any data type), and it will continue the execution without waiting for the process to complete. But still, we need to know when the overall execution of all the processes completes to continue with the main thread execution. For that, we are making the following changes (&lt;a href="https://github.com/sgisbert/parallelization/blob/b8836463629fa872c1a6b78c25413cacc2e0776e/src/Program.cs" target="_blank" rel="noopener"&gt;see full file on Github&lt;/a&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We declare a list of &lt;em&gt;Task&lt;/em&gt;&amp;nbsp;where we'll keep track of all the &lt;em&gt;Task&lt;/em&gt;&amp;nbsp;being created as the processes are started:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;List&amp;lt;Task&amp;gt; tasks = new List&amp;lt;Task&amp;gt;();
for (int i = 0; i &amp;lt; 10; i++)
{
     tasks.Add(Process(i));
}
Task.WaitAll(tasks.ToArray());&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;We wait for all the tasks to complete with &lt;em&gt;Task.WaitAll()&lt;br /&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With these changes, we can run the program again and, surprise! We still have the same poor performance as at the start... What happened?&lt;/p&gt;
&lt;p&gt;This is because the compiler needs the combination of both &lt;em&gt;async/await&lt;/em&gt; keywords in a method to start a new execution thread. In this simple case, the &lt;em&gt;Process()&lt;/em&gt;&amp;nbsp;method is declared as &lt;em&gt;async&lt;/em&gt;, but it is not making use of any &lt;em&gt;await&lt;/em&gt; call, so the compiler will just treat it as a synchronous method. We will even be getting a warning from the compiler at editing time:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image1_637476153425009174.png" alt="Image1.png" width="570" height="311" /&gt;&lt;/p&gt;
&lt;p&gt;If as part of the method we would make use of any other asynchronous calls, like reading some data from a DB with EF Core, then we would get a new execution thread and the following step wouldn't be necessary.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Executing the processes in parallel&lt;/h2&gt;
&lt;p&gt;To make sure we are getting a new thread for each process, we are modifying the code like this (&lt;a href="https://github.com/sgisbert/parallelization/blob/a4bf00fb77a579530a91d0c0be4a9901e6d44e57/src/Program.cs" target="_blank" rel="noopener"&gt;see full file on Github&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;private static async Task Process(int id)
{
    await Task.Run(() =&amp;gt;
    {
        Stopwatch timer = new Stopwatch();
        timer.Start();
        Thread.Sleep(200);
        Console.WriteLine($"Process {id}: {timer.Elapsed}");
    });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way, we use &lt;em&gt;await&lt;/em&gt;&amp;nbsp;to create a new thread with&amp;nbsp;&lt;em&gt;Task.Run()&lt;/em&gt;.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;When we run the application again, these are the results:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;Process 2: 00:00:00.2016508
Process 3: 00:00:00.2010563
Process 1: 00:00:00.2016712
Process 0: 00:00:00.2053744
Process 7: 00:00:00.2008342
Process 6: 00:00:00.2008357
Process 5: 00:00:00.2008776
Process 4: 00:00:00.2009419
Process 8: 00:00:00.2091808
Process 9: 00:00:00.2091704
Completed: 00:00:00.6359204
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally we can appreciate a notorious performance increase, as we moved from the original&amp;nbsp;&lt;strong&gt;2 seconds &lt;/strong&gt;down&amp;nbsp;to&amp;nbsp;&lt;strong&gt;0,6 seconds&lt;/strong&gt;&amp;nbsp;with the parallelized execution. Note as well that the execution order is randomly displayed, as the process do not rely any more on the starting order, but the time it takes to each one to complete.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Using this fairly simple parallelization technique, we can dramatically increase the performance in our applications.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;We need to understand correctly how async/await works, because, as demostrated, simply converting a method into &lt;em&gt;async&lt;/em&gt; and invoking it with&lt;em&gt;&amp;nbsp;await&lt;/em&gt; is not enough. You have to use&amp;nbsp;&lt;em&gt;Task.WaitAll()&lt;/em&gt;&amp;nbsp;for this.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;Generally speaking, when you find some processes that do not rely on each other, you may be able to parallelize them, as for example, make a DB query and loading some data from an XML file, or making a request to an external API.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;Tip: if you see a bunch of&amp;nbsp;&lt;em&gt;await&lt;/em&gt; calls in a row, think if they can be parallelized.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;If a process needs the outcome of another process as an input, these cannot be parallelized.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;Not all cases can be parallelized, like for instance, using EF Core to access a DB will not allow to make two simultaneous requests using the same context.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;However, working with the file system is a great candidate to process files in parallel and getting some performance gains.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the &lt;a href="https://www.sergigisbert.com/blog/make-use-of-concurrentbag-to-store-the-results-from-asynchronous-processes/"&gt;following blog post&lt;/a&gt; I explain how to use Thread-safe data types to interact with them from different threads, as for instance, make some parallel calculations and get all together back into the same List.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://stage.sergigisbert.com/blog/mejorar-el-rendimiento-con-funciones-asincronas-para-la-ejecucion-de-procesos-en-paralelo/</id>
    <title>Mejorar el rendimiento con funciones asíncronas para la ejecución de procesos en paralelo</title>
    <updated>2021-02-13T16:05:24Z</updated>
    <published>2021-01-30T10:30:23Z</published>
    <link href="https://stage.sergigisbert.com/blog/mejorar-el-rendimiento-con-funciones-asincronas-para-la-ejecucion-de-procesos-en-paralelo/" />
    <author>
      <name>test@example.com</name>
      <email>Sergi Gisbert</email>
    </author>
    <category term="c#" />
    <category term="dotnet core" />
    <category term="performance" />
    <category term="tips" />
    <category term="async/await" />
    <content type="html">&lt;p&gt;&lt;span style="color: #808080;"&gt;&lt;em&gt;Disclaimer: los m&amp;eacute;todos y ejemplos de c&amp;oacute;digo de este art&amp;iacute;culo son fruto de mi propia investigaci&amp;oacute;n y aprendizaje aut&amp;oacute;nomo, y en ning&amp;uacute;n caso est&amp;aacute;n preparados para su utilizaci&amp;oacute;n en c&amp;oacute;digo real de producci&amp;oacute;n. El autor no se hace responsable del uso de estos ejemplos. Para m&amp;aacute;s informaci&amp;oacute;n sobre programaci&amp;oacute;n paralela y as&amp;iacute;ncrona y sus connotaciones, recomiendo acceder a la documentaci&amp;oacute;n oficial: &lt;a style="color: #808080;" href="https://docs.microsoft.com/es-es/dotnet/standard/parallel-processing-and-concurrency" target="_blank" rel="noopener"&gt;https://docs.microsoft.com/es-es/dotnet/standard/parallel-processing-and-concurrency&lt;/a&gt;&amp;nbsp;&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;span style="color: #993300;"&gt;Este post forma parte de una serie de 3 art&amp;iacute;culos sobre mejoras de rendimiento utilizando programaci&amp;oacute;n paralela:&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;span style="color: #993300;"&gt;1. Mejorar el rendimiento con funciones as&amp;iacute;ncronas para la ejecuci&amp;oacute;n de procesos en paralelo (Este art&amp;iacute;culo)&lt;br /&gt;&lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #993300;"&gt;2.&amp;nbsp;&lt;a style="color: #993300;" href="https://www.sergigisbert.com/blog/utilizar-concurrentbag-para-almacenar-el-resultado-de-metodos-asincronos/" target="_blank" rel="noopener"&gt;Utilizar ConcurrentBag para almacenar el resultado de m&amp;eacute;todos as&amp;iacute;ncronos&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/em&gt;&lt;em&gt;&lt;span style="color: #993300;"&gt;3. &lt;a style="color: #993300;" href="https://www.sergigisbert.com/blog/programacion-paralela-en-c-con-la-clase-parallel/" target="_blank" rel="noopener"&gt;Programaci&amp;oacute;n paralela en C# con la clase Parallel&lt;/a&gt;&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Recientemente tuve que colaborar con un compa&amp;ntilde;ero de trabajo porque la ejecuci&amp;oacute;n de un proceso era m&amp;aacute;s lenta de lo esperado. Repasando el c&amp;oacute;digo, vimos que parte del proceso se pod&amp;iacute;a realizar en paralelo, con lo que el rendimiento general mejor&amp;oacute; en un 200%. En este post intento explicar algunas t&amp;eacute;cnicas b&amp;aacute;sicas para trabajar con paralelizaci&amp;oacute;n de procesos y mostrar&amp;eacute; las mejoras de rendimiento que se pueden obtener.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Caso inicial&lt;/h2&gt;
&lt;p&gt;Vamos a utilizar una simple aplicaci&amp;oacute;n de consola C# para ilustrar un caso sencillo, y c&amp;oacute;mo vamos a mejorar su rendimiento utilizando t&amp;eacute;cnicas de computaci&amp;oacute;n paralalela. El c&amp;oacute;digo fuente lo pod&amp;eacute;is encontrar aqu&amp;iacute;:&amp;nbsp;&lt;a href="https://github.com/sgisbert/parallelization" target="_blank" rel="noopener"&gt;https://github.com/sgisbert/parallelization&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;El c&amp;oacute;digo inicial ser&amp;aacute; el siguiente (&lt;a href="https://github.com/sgisbert/parallelization/blob/4c3b57b019822c9bd6000bc760511276c9b25351/src/Program.cs" target="_blank" rel="noopener"&gt;Ver en Github&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;using System.Threading;
using System.Diagnostics;
using System;

namespace parallel
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch timer = new Stopwatch();
            timer.Start();

            for (int i = 0; i &amp;lt; 10; i++)
            {
                Process(i);
            }

            Console.WriteLine($"Completed: {timer.Elapsed}");
        }

        private static void Process(int id)
        {
            Stopwatch timer = new Stopwatch();
            timer.Start();
            Thread.Sleep(200);
            Console.WriteLine($"Process {id}: {timer.Elapsed}");
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tenemos un proceso que tarda 200ms en ejecutarse, y lo ejecutamos 10 veces, de forma secuencial con un bucle &lt;em&gt;for&lt;/em&gt;. Como cabr&amp;iacute;a esperar, la ejecuci&amp;oacute;n de este c&amp;oacute;digo tarda unos 2 segundos en completarse, ya que cada proceso tiene que esperar a que termine el anterior para iniciarse:&amp;nbsp;&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;Process 0: 00:00:00.2018134
Process 1: 00:00:00.2003564
Process 2: 00:00:00.2003647
Process 3: 00:00:00.2007673
Process 4: 00:00:00.2008702
Process 5: 00:00:00.2004851
Process 6: 00:00:00.2003682
Process 7: 00:00:00.2009726
Process 8: 00:00:00.2009657
Process 9: 00:00:00.2006482
Completed: 00:00:02.0362772&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Convertir el m&amp;eacute;todo en as&amp;iacute;ncrono&lt;/h2&gt;
&lt;p&gt;El objetivo es que estos 10 procesos se puedan lanzar de manera as&amp;iacute;ncrona y en paralelo, de manera que no tengan que esperarse unos a otros para arrancar. El primer paso ser&amp;aacute; convertir el m&amp;eacute;todo&amp;nbsp;&lt;em&gt;Process()&lt;/em&gt; en una funci&amp;oacute;n as&amp;iacute;ncrona, por lo que hacemos los siguientes cambios (&lt;a href="https://github.com/sgisbert/parallelization/blob/fdb32905ee5347c37052bc7c3cc129a990dcf8fb/src/Program.cs" target="_blank" rel="noopener"&gt;ver archivo completo en Github&lt;/a&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cambiar la signatura del m&amp;eacute;todo:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;private static async Task Process(int id)&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Cambiar la llamada:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;await Process(i);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Cambiar la signatura del m&amp;eacute;todo Main para que tambi&amp;eacute;n sea as&amp;iacute;ncrono:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;static async Task Main(string[] args)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Con esto ya tenemos nuestro m&amp;eacute;todo as&amp;iacute;ncrono, pero, &amp;iquest;ha mejorado el rendimiento? Si lo volvemos a ejecutar, obtenemos exactamente los mismos valores que en la ejecuci&amp;oacute;n anterior, es decir, seguimos con el mismo problema.&lt;/p&gt;
&lt;p&gt;Esto es debido a que estamos llamando al proceso con el modificador&amp;nbsp;&lt;em&gt;await&lt;/em&gt;, con lo que literalmente le estamos diciendo "espera a que termine para continuar". Es decir, estamos llamando a un m&amp;eacute;todo as&amp;iacute;ncrono de manera s&amp;iacute;ncrona, y no es lo que queremos.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Ejecutar el proceso de manera as&amp;iacute;ncrona&lt;/h2&gt;
&lt;p&gt;Para ejecutar el proceso de manera as&amp;iacute;ncrona, debemos llamar al m&amp;eacute;todo sin el modificador&amp;nbsp;&lt;em&gt;await&lt;/em&gt;. En este caso, la funci&amp;oacute;n devuelve un objeto&amp;nbsp;&lt;em&gt;Task&lt;/em&gt; en lugar del resultado de la misma (aunque era void en este caso, pero podr&amp;iacute;a devolver cualquier tipo de datos), y va a seguir la ejecuci&amp;oacute;n sin esperar a que &amp;eacute;sta finalice. Pero igualmente necesitamos saber cu&amp;aacute;ndo termina la ejecuci&amp;oacute;n de todos los procesos para seguir con la ejecuci&amp;oacute;n principal. Para ello, hacemos los siguientes cambios (&lt;a href="https://github.com/sgisbert/parallelization/blob/b8836463629fa872c1a6b78c25413cacc2e0776e/src/Program.cs" target="_blank" rel="noopener"&gt;ver archivo completo en Github&lt;/a&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creamos una lista de&amp;nbsp;&lt;em&gt;Task&lt;/em&gt; donde iremos guardando las&amp;nbsp;&lt;em&gt;Task&lt;/em&gt; que se vayan creando conforme vamos lanzando los procesos&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;List&amp;lt;Task&amp;gt; tasks = new List&amp;lt;Task&amp;gt;();
for (int i = 0; i &amp;lt; 10; i++)
{
     tasks.Add(Process(i));
}
Task.WaitAll(tasks.ToArray());&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Esperamos a que todas las tareas terminen mediante&amp;nbsp;&lt;em&gt;Task.WaitAll()&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Con estos cambios, volvemos a lanzar el programa y, &amp;iexcl;sorpresa! Seguimos teniendo el mismo rendimiento que al principio. &amp;iquest;Qu&amp;eacute; ha pasado?&lt;/p&gt;
&lt;p&gt;Esto es debido a que el compilador necesita de la combinaci&amp;oacute;n de async/await en un m&amp;eacute;todo para lanzar un nuevo hilo de ejecuci&amp;oacute;n. En este caso sencillo, la funci&amp;oacute;n&amp;nbsp;&lt;em&gt;Process()&lt;/em&gt; est&amp;aacute; declarada como&amp;nbsp;&lt;em&gt;async&lt;/em&gt;, pero no hace ninguna llamada&amp;nbsp;&lt;em&gt;await&lt;/em&gt;, por lo que el compilador la va a ejecutar de manera s&amp;iacute;ncrona. El propio compilador nos avisar&amp;aacute; con un warning:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image1_637476153425009174.png" alt="Image1.png" width="570" height="311" /&gt;&lt;/p&gt;
&lt;p&gt;Si dentro de este m&amp;eacute;todo hici&amp;eacute;ramos uso de otras llamadas as&amp;iacute;ncronas, como leer registros de una BD con EF Core, por ejemplo, entonces s&amp;iacute; que tendr&amp;iacute;amos un nuevo hilo de ejecuci&amp;oacute;n y el paso siguiente no ser&amp;iacute;a necesario.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Ejecutar el proceso en paralelo&lt;/h2&gt;
&lt;p&gt;Para asegurarnos de que el proceso se inicia en un hilo diferente en cada caso, modificamos el m&amp;eacute;todo de la siguiente manera (&lt;a href="https://github.com/sgisbert/parallelization/blob/a4bf00fb77a579530a91d0c0be4a9901e6d44e57/src/Program.cs" target="_blank" rel="noopener"&gt;ver archivo completo en Github&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;private static async Task Process(int id)
{
    await Task.Run(() =&amp;gt;
    {
        Stopwatch timer = new Stopwatch();
        timer.Start();
        Thread.Sleep(200);
        Console.WriteLine($"Process {id}: {timer.Elapsed}");
    });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;De esta manera, utilizamos&amp;nbsp;&lt;em&gt;await&lt;/em&gt; pero lanzando un nuevo hilo con&amp;nbsp;&lt;em&gt;Task.Run()&lt;/em&gt;.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Cuando ejecutamos el proceso principal, estos son los resultados:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;Process 2: 00:00:00.2016508
Process 3: 00:00:00.2010563
Process 1: 00:00:00.2016712
Process 0: 00:00:00.2053744
Process 7: 00:00:00.2008342
Process 6: 00:00:00.2008357
Process 5: 00:00:00.2008776
Process 4: 00:00:00.2009419
Process 8: 00:00:00.2091808
Process 9: 00:00:00.2091704
Completed: 00:00:00.6359204
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finalmente s&amp;iacute; podemos apreciar una mejora notable, ya que hemos pasado de los &lt;strong&gt;2 segundos&lt;/strong&gt; iniciales a &lt;strong&gt;0,6 segundos&lt;/strong&gt; con la ejecuci&amp;oacute;n en paralelo. Notar igualmente que el orden en el que termina cada proceso es aleatorio, ya que ya no dependen del orden en el que se ejecutan, sino del tiempo que tarda cada uno de ellos.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Conclusiones&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Utilizando esta sencilla t&amp;eacute;cnica de paralelizaci&amp;oacute;n podemos aumentar dr&amp;aacute;sticamente el rendimiento de nuestras aplicaciones.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;Hay que saber aplicarlo correctamente, porque simplemente convirtiendo un m&amp;eacute;todo en&amp;nbsp;&lt;em&gt;async&lt;/em&gt; y llamarlo con&amp;nbsp;&lt;em&gt;await&lt;/em&gt; no es suficiente, como hemos visto en el ejemplo. Usamos&amp;nbsp;&lt;em&gt;Task.WaitAll()&lt;/em&gt; para esto.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;En general, cuando tengamos procesos que no dependan unos de otros, podremos paralelizarlos, como por ejemplo, hacer una consulta a BD mientras cargamos unos textos de un archivo XML o llamamos a un servicio de una API externa.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;Si ves unas cuantas llamadas seguidas con&amp;nbsp;&lt;em&gt;await&lt;/em&gt;, piensa si se pueden paralelizar.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;Si un proceso dependo de otro, no se podr&amp;aacute; paralelizar.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;No en todos los casos se puede paralelizar, por ejemplo, si usamos EF Core para el acceso a BD, no podemos tener dos llamadas simult&amp;aacute;neas con el mismo contexto.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;En cambio, si trabajamos con archivos, podemos procesarlos de manera paralela y mejorar as&amp;iacute; el rendimiento.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En la &lt;a href="https://www.sergigisbert.com/blog/utilizar-concurrentbag-para-almacenar-el-resultado-de-metodos-asincronos/"&gt;siguiente entrada&lt;/a&gt; del blog comentamos c&amp;oacute;mo usar tipos de datos Thread-Safe para interactuar con ellos desde diferentes hilos, como por ejemplo, realizar procesos en paralelo que nos devuelvan el resultado en una misma Lista.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://stage.sergigisbert.com/blog/validacion-de-api-requests-y-json-schemas-con-postman/</id>
    <title>Validación de API requests y JSON Schemas con Postman</title>
    <updated>2021-01-30T17:29:22Z</updated>
    <published>2021-01-30T10:00:00Z</published>
    <link href="https://stage.sergigisbert.com/blog/validacion-de-api-requests-y-json-schemas-con-postman/" />
    <author>
      <name>test@example.com</name>
      <email>Sergi Gisbert</email>
    </author>
    <category term="api" />
    <category term="postman" />
    <category term="json" />
    <category term="json schema" />
    <content type="html">&lt;p&gt;&lt;a href="https://www.postman.com/" target="_blank" rel="noopener"&gt;Postman&lt;/a&gt; es una herramienta muy &amp;uacute;til para probar las peticiones a APIs, tanto si las estamos desarrollando nosotros, como si son de otro proveedor, y poder as&amp;iacute; testear y comprobar los datos que se reciben en cada llamada. En este post, abordar&amp;eacute; c&amp;oacute;mo incluir tests de validaci&amp;oacute;n para comprobar que las llamadas se reciben correctamente y que adem&amp;aacute;s, los datos que recibimos cumplen con un determinado schema JSON predefinido.&lt;/p&gt;
&lt;p&gt;Este post forma parte de la serie de art&amp;iacute;culos sobre Postman, que empezamos con la &lt;a href="https://www.sergigisbert.com/blog/como-automatizar-la-autenticacion-de-llamadas-a-api-con-bearer-token-usando-postman/"&gt;automatizaci&amp;oacute;n de la autenticaci&amp;oacute;n de APIs&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Comprobaci&amp;oacute;n del resultado de la llamada&lt;/h2&gt;
&lt;p&gt;El primer test y el m&amp;aacute;s sencillo que podemos incorporar es validar que la llamada se ha realizado correctamente, y hemos recibido un &lt;strong&gt;&lt;em&gt;Response 200 OK&lt;/em&gt;&lt;/strong&gt;, o cualquier otro que estemos esperando. Para ello, la manera m&amp;aacute;s sencilla es incluir este test a nivel de Collection, para que as&amp;iacute; todas las peticiones incluidas en la misma hereden este test.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Para ello, editamos la colecci&amp;oacute;n y en la pesta&amp;ntilde;a "&lt;strong&gt;Tests&lt;/strong&gt;", incluimos el siguiente script:&lt;/p&gt;
&lt;pre class="language-javascript"&gt;&lt;code&gt;pm.test("Status code is OK", function () {
    pm.expect(pm.response.code).to.be.oneOf([200, 201, 204, 207])
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En la lista de c&amp;oacute;digos de respuesta podemos incluir todos los que necesitemos. Una vez configurado, si ejecutamos cualquier petici&amp;oacute;n, podemos ver en la pesta&amp;ntilde;a "&lt;strong&gt;Tests&lt;/strong&gt;" de la respuesta el resultado del mismo:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image1_637470015340948668.png" alt="Image1.png" width="340" height="126" /&gt;&lt;/p&gt;
&lt;h2&gt;Validaci&amp;oacute;n del schema JSON de la respuesta&lt;/h2&gt;
&lt;p&gt;El lenguage de scripting de Postman es muy vers&amp;aacute;til, y nos permite ir m&amp;aacute;s all&amp;aacute; de una simple comprobaci&amp;oacute;n del estado de la respuesta, y podemos incluir validaciones m&amp;aacute;s complejas, como por ejemplo, comprobar que el contenido de la respuesta en JSON cumple con el schema predefinido que debe devolver dicha petici&amp;oacute;n.&lt;/p&gt;
&lt;p&gt;Para poder ejecutar esta validaci&amp;oacute;n, volvemos a la pesta&amp;ntilde;a "&lt;strong&gt;Tests&lt;/strong&gt;" de la Collection, y a&amp;ntilde;adimos el siguiente script, a continuaci&amp;oacute;n del script anterior:&lt;/p&gt;
&lt;pre class="language-javascript"&gt;&lt;code&gt;try 
{
    var schema = pm.variables.get("schema");

    if(schema)
    {
        const jsonData = pm.response.json();
        if(jsonData)
        {
            //const dateTimeRegex = new RegExp('^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?$');
            var Ajv = require('ajv'),
            ajv = new Ajv({logger: console, allErrors: true});
            //ajv.addFormat('date-time', {
            //    validate: (dateTimeString) =&amp;gt; dateTimeRegex.test(dateTimeString)
            //});
        
            pm.test('Schema is valid', function() {
                var validate = ajv.validate(schema, jsonData);
                console.log(ajv.errors);
                pm.expect(validate, JSON.stringify(ajv.errors)).to.be.true;
            });
        }
    }
}
catch(e){
    console.log(e);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Este script obtendr&amp;aacute; el schema de una variable "&lt;strong&gt;schema&lt;/strong&gt;", que luego definiremos, y validar&amp;aacute; el contenido de la respuesta de la petici&amp;oacute;n contra dicho schema, utilizando la librer&amp;iacute;a "&lt;a href="https://github.com/ajv-validator/ajv" target="_blank" rel="noopener"&gt;Ajv&lt;/a&gt;". Como a&amp;ntilde;adido, he dejado comentado c&amp;oacute;mo definir un formato de fecha espec&amp;iacute;fico, para validaciones que requieran un formato de fecha diferente.&lt;/p&gt;
&lt;p&gt;El siguiente paso ser&amp;aacute; obtener el schema que debe devolver la petici&amp;oacute;n. Si disponemos del detalle del API que estamos consumiendo en en servicios como &lt;a href="https://apiary.io/" target="_blank" rel="noopener"&gt;Apiary&lt;/a&gt; o &lt;a href="https://swagger.io/" target="_blank" rel="noopener"&gt;Swagger&lt;/a&gt;, estos mismos servicios ya nos proporcionan el detalle del schema en su definici&amp;oacute;n de cada petici&amp;oacute;n. Si no disponemos de ellos, pero tenemos un objeto JSON v&amp;aacute;lido, podemos utilizar herramientas online como &lt;a href="https://app.quicktype.io/" target="_blank" rel="noopener"&gt;Quicktype&lt;/a&gt;, para generar nuestro schema. En este ejemplo, pod&amp;eacute;is ver el schema generado para un json del API de Zendesk:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image2 (Custom)_637470018453262659.png" alt="Image2 (Custom).png" width="800" height="342" /&gt;&lt;/p&gt;
&lt;p&gt;Una vez que ya tenemos el schema, volvemos a Postman y seleccionamos la petici&amp;oacute;n sobre la cual queremos validar su schema, y editamos la pesta&amp;ntilde;a "&lt;strong&gt;Pre-request Script&lt;/strong&gt;", donde incluimos el siguiente script (&lt;em&gt;usando el schema obtenido con antelaci&amp;oacute;n&lt;/em&gt;):&lt;/p&gt;
&lt;pre class="language-javascript"&gt;&lt;code&gt;var schema = {
    "$schema": "http://json-schema.org/draft-06/schema#",
    "$ref": "#/definitions/ZendeskJobResult",
    "definitions": {
        "ZendeskJobResult": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "action": { "type": "string" },
                "id": { "type": "integer" },
                "status": { "type": "string" },
                "success": { "type": "boolean" }
            },
            "required": ["action", "id", "status", "success" ],
            "title": "ZendeskJobResult"
        }
    }
};

pm.variables.set("schema", schema);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Por defecto, es posible que nos defina todos los campos como "Required", por lo que tendremos que validar manualmente si es correcto, y modificarlo seg&amp;uacute;n nuestras necesidades. Estamos a&amp;ntilde;adiendo el schema a una variable local, que luego guardamos como variable de ejecuci&amp;oacute;n.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Este proceso tendremos que hacerlo para cada una de las peticiones que queramos validar, ya que por norma general, el schema de los datos devueltos por cada petici&amp;oacute;n ser&amp;aacute; diferente, pero s&amp;oacute;lo tenemos que definir el schema, ya que el script de validaci&amp;oacute;n lo hemos definido a nivel de Collection.&lt;/p&gt;
&lt;p&gt;El funcionamiento es el siguiente:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Antes de iniciarse la petici&amp;oacute;n, el script de "&lt;strong&gt;Pre-request Script&lt;/strong&gt;" de la petici&amp;oacute;n se ejecuta, y define el valor de la variable "&lt;strong&gt;schema&lt;/strong&gt;"&lt;/li&gt;
&lt;li&gt;Despu&amp;eacute;s de ejecutarse la petici&amp;oacute;n, el script de "&lt;strong&gt;Tests&lt;/strong&gt;" de la Collection se ejecuta, y utiliza la variable definida por la petici&amp;oacute;n para validar la misma&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Como resultado, podemos ver que la pesta&amp;ntilde;a de &lt;strong&gt;Tests&lt;/strong&gt;&amp;nbsp;de la respuesta se ha ampliado con un segundo test:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image3_637470015343009242.png" alt="Image3.png" width="319" height="160" /&gt;&lt;/p&gt;
&lt;p&gt;En el caso de que no valide el schema, nos indicar&amp;aacute; cu&amp;aacute;l ha sido la propiedad que no cumple y el motivo:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image6_637470015343140653.png" alt="Image6.png" width="765" height="188" /&gt;&lt;/p&gt;
&lt;p&gt;En este caso, podemos ver que falta la propiedad "serviceList", que estaba marcada como requerida.&lt;/p&gt;
&lt;h2&gt;Ejecuci&amp;oacute;n de tests masivos&lt;/h2&gt;
&lt;p&gt;Una vez tenemos definidos estos tests de validaci&amp;oacute;n, podemos hacer uso de otra de las herramientas que incluye Postman para ejecutar validaci&amp;oacute;n por lotes de todas o un conjunto de las peticiones definidas en una Collection. Para ello, abrimos la herramienta &lt;strong&gt;Runner&lt;/strong&gt;:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image4_637470015343257941.png" alt="Image4.png" width="296" height="50" /&gt;&lt;/p&gt;
&lt;p&gt;Elegimos la Collection a ejecutar, o la carpeta que queramos lanzar, incluso qu&amp;eacute; peticiones incluimos en la ejecuci&amp;oacute;n, el Environment sobre el que lo ejecutamos, as&amp;iacute; como el n&amp;uacute;mero de veces que se va a lanzar, el delay entre cada petici&amp;oacute;n y algunos settings adicionales. Runner se encargar&amp;aacute; de lanzar todas las peticiones por nosotros, y mostrarnos un resumen de resultados de las mismas:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image5_637470015343403236.png" alt="Image5.png" width="938" height="687" /&gt;&lt;/p&gt;
&lt;p&gt;De esta manera, podemos lanzar de una forma sencilla todas nuestras peticiones y comprobar que todo sigue funcionando correctamente despu&amp;eacute;s de, por ejemplo, un despliegue a producci&amp;oacute;n.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://stage.sergigisbert.com/blog/validating-api-requests-and-json-schemas-with-postman/</id>
    <title>Validating API Requests and JSON schemas with Postman</title>
    <updated>2021-01-30T17:31:31Z</updated>
    <published>2021-01-30T10:00:00Z</published>
    <link href="https://stage.sergigisbert.com/blog/validating-api-requests-and-json-schemas-with-postman/" />
    <author>
      <name>test@example.com</name>
      <email>Sergi Gisbert</email>
    </author>
    <category term="english" />
    <category term="json schema" />
    <category term="json" />
    <category term="postman" />
    <category term="api" />
    <content type="html">&lt;p&gt;&lt;span style="background-color: #e6e6e6;"&gt;&lt;em&gt;You can find the Spanish version of this post &lt;a href="https://www.sergigisbert.com/blog/validacion-de-api-requests-y-json-schemas-con-postman/"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.postman.com/" target="_blank" rel="noopener"&gt;Postman&lt;/a&gt;&amp;nbsp;is a useful tool to test API requests, whether we are developing our own APIs, as well as we are using a third party provider APIs, so that we can check all the data received on each request. In this post, I will explain how to include validation tests along with each request, to check the calls are succesful and also that the json data received meets the expected JSON schema agreed.&lt;/p&gt;
&lt;p&gt;This post is part of a series about Postman, started with &lt;a href="https://www.sergigisbert.com/blog/how-to-automate-api-requests-authorization-with-postman-using-bearer-tokens/"&gt;Automatic API authentication&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Check request result&lt;/h2&gt;
&lt;p&gt;The first and simpler test that we can include is to validate the request has succeded, and we got the &lt;strong&gt;&lt;em&gt;Response 200 OK&lt;/em&gt;&lt;/strong&gt;, or any other response code we may be expecting. To do so, the easiest way is to include the test at the Collection level, so that all the requests underneath will inherit this validation.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;So you can edit the Collection, select the "&lt;strong&gt;Tests&lt;/strong&gt;" tab, and include the following script:&lt;/p&gt;
&lt;pre class="language-javascript"&gt;&lt;code&gt;pm.test("Status code is OK", function () {
    pm.expect(pm.response.code).to.be.oneOf([200, 201, 204, 207])
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can include any other response codes you might be expecting from your requests as well.&lt;/p&gt;
&lt;p&gt;Once set up, on any request execution, you should see the test result in the response "&lt;strong&gt;Tests&lt;/strong&gt;" tab:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image1_637470015340948668.png" alt="Image1.png" width="340" height="126" /&gt;&lt;/p&gt;
&lt;h2&gt;Response JSON schema validation&lt;/h2&gt;
&lt;p&gt;Postman scripting is quite a powerful asset, so you can go beyond a simple response code validation and include more complex validations, like checking that the response JSON meets a determined schema.&lt;/p&gt;
&lt;p&gt;In order to execute such validation, back to the "&lt;strong&gt;Tests&lt;/strong&gt;" tab in the Collection to include the following script, right after the previous script we added in the previous step:&lt;/p&gt;
&lt;pre class="language-javascript"&gt;&lt;code&gt;try 
{
    var schema = pm.variables.get("schema");

    if(schema)
    {
        const jsonData = pm.response.json();
        if(jsonData)
        {
            //const dateTimeRegex = new RegExp('^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?$');
            var Ajv = require('ajv'),
            ajv = new Ajv({logger: console, allErrors: true});
            //ajv.addFormat('date-time', {
            //    validate: (dateTimeString) =&amp;gt; dateTimeRegex.test(dateTimeString)
            //});
        
            pm.test('Schema is valid', function() {
                var validate = ajv.validate(schema, jsonData);
                console.log(ajv.errors);
                pm.expect(validate, JSON.stringify(ajv.errors)).to.be.true;
            });
        }
    }
}
catch(e){
    console.log(e);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This script will get the schema from the "&lt;strong&gt;schema&lt;/strong&gt;" variable, that we'll set up later, and will validate the response content against it, using the "&lt;a href="https://github.com/ajv-validator/ajv" target="_blank" rel="noopener"&gt;Ajv&lt;/a&gt;" library. As an add-on, I left commented out how to include a specific date formatting validation rule, in cases where you might need custom formats for some data types.&lt;/p&gt;
&lt;p&gt;Next step will be to get the schema itself that we are expecting the response to return. If you have access to the API service details, with tools like&amp;nbsp;&lt;a href="https://apiary.io/" target="_blank" rel="noopener"&gt;Apiary&lt;/a&gt;&amp;nbsp;or&amp;nbsp;&lt;a href="https://swagger.io/" target="_blank" rel="noopener"&gt;Swagger&lt;/a&gt;, you can get the schema details from them. If not, but you have a valid JSON object, you can use online tools like&amp;nbsp;&lt;a href="https://app.quicktype.io/" target="_blank" rel="noopener"&gt;Quicktype&lt;/a&gt;&amp;nbsp;to generate your own schema. In the following screenshot, you can see the schema generated with a JSON from the Zendesk API:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image2 (Custom)_637470018453262659.png" alt="Image2 (Custom).png" width="800" height="342" /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Once you got the schema, back to Postman, select the request you want to be validated, and edit the "&lt;strong&gt;Pre-request Script&lt;/strong&gt;" tab, where you should add the following script (&lt;em&gt;using the schema from the step before&lt;/em&gt;):&lt;/p&gt;
&lt;pre class="language-javascript"&gt;&lt;code&gt;var schema = {
    "$schema": "http://json-schema.org/draft-06/schema#",
    "$ref": "#/definitions/ZendeskJobResult",
    "definitions": {
        "ZendeskJobResult": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "action": { "type": "string" },
                "id": { "type": "integer" },
                "status": { "type": "string" },
                "success": { "type": "boolean" }
            },
            "required": ["action", "id", "status", "success" ],
            "title": "ZendeskJobResult"
        }
    }
};

pm.variables.set("schema", schema);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default, it is possible that all the properties may be included in the "required" array, so you will need to manually check if that fits your needs. You are adding the schema to a local variable, and then storing it as a runtime variable.&lt;/p&gt;
&lt;p&gt;You need to do this process for every single request you want to validate, as normally the response data schema will be different on every request, but this is the only step you need to do, as the validation script itself is shared across the Collection.&lt;/p&gt;
&lt;p&gt;It works in the following way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Before the request is sent, the "&lt;strong&gt;Pre-request Script&lt;/strong&gt;" is executed and the "&lt;strong&gt;schema&lt;/strong&gt;" variable is stored.&lt;/li&gt;
&lt;li&gt;After the request is executed, the script in Collection "&lt;strong&gt;Tests&lt;/strong&gt;" tab is executed, and will use the variable set up by the request to validate the response data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a result, you should see in the response&amp;nbsp;&lt;strong&gt;Tests&lt;/strong&gt;&amp;nbsp;tab a second test included:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image3_637470015343009242.png" alt="Image3.png" width="319" height="160" /&gt;&lt;/p&gt;
&lt;p&gt;In the event that the data is not valid, you should see the wrong property and the reason why the schema was not met:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image6_637470015343140653.png" alt="Image6.png" width="765" height="188" /&gt;&lt;/p&gt;
&lt;p&gt;In this case, you can see the "serviceList" property is missing, but it was defined as "required".&lt;/p&gt;
&lt;h2&gt;Tests execution in batches&lt;/h2&gt;
&lt;p&gt;Once validation tests are defined, we can make use of another tool included with Postman to run a batch execution of all or a selected subset of requests defined in a Collection. To do so, you can use&amp;nbsp;&lt;strong&gt;Runner&lt;/strong&gt;:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image4_637470015343257941.png" alt="Image4.png" width="296" height="50" /&gt;&lt;/p&gt;
&lt;p&gt;You should select the Collection to execute, or the folder, or even the specific requests you want to include in the bath; then the Environment used, and the number of executions, the delay between each request, and some other additional settings. Runner will execute all the requests for us, and will present us with a report on finalization:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://sergiwebstorage.blob.core.windows.net/blogfiles/Posts\files\Image5_637470015343403236.png" alt="Image5.png" width="938" height="687" /&gt;&lt;/p&gt;
&lt;p&gt;This way, we can easily run all our requests and check that everything is still fine after a deployment into Production, for example.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</content>
  </entry></feed>