<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Andres Lozada Mosto</title>
    <description>The latest articles on DEV Community by Andres Lozada Mosto (@andreslozadamosto).</description>
    <link>https://dev.to/andreslozadamosto</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F104023%2Fd452a111-ea81-4291-b76f-8130057c434a.jpg</url>
      <title>DEV Community: Andres Lozada Mosto</title>
      <link>https://dev.to/andreslozadamosto</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/andreslozadamosto"/>
    <language>en</language>
    <item>
      <title>Midiendo el rendimiento de nuestro código con BenchmarkDotNet - MemoryDiagnoser</title>
      <dc:creator>Andres Lozada Mosto</dc:creator>
      <pubDate>Sun, 16 Jul 2023 16:52:08 +0000</pubDate>
      <link>https://dev.to/andreslozadamosto/midiendo-el-rendimiento-de-nuestro-codigo-con-benchmarkdotnet-parte-2-58lc</link>
      <guid>https://dev.to/andreslozadamosto/midiendo-el-rendimiento-de-nuestro-codigo-con-benchmarkdotnet-parte-2-58lc</guid>
      <description>&lt;p&gt;En el primer artículo de esta serie introducimos el concepto de &lt;em&gt;Benchmark&lt;/em&gt; y como crear un test de rendimiento en C#. Es el momento de entender como analizar la utilización de la memoria y del GC de .Net.&lt;/p&gt;

&lt;h1&gt;
  
  
  Utilización de MemoryDiagnoser
&lt;/h1&gt;

&lt;p&gt;En este caso vamos a utilizar un &lt;em&gt;Benchmark&lt;/em&gt; diferente al que utilizamos anteriormente para que nos permita ver cuanta memoria es utilizada y cuantas veces se ejecuta el GC.&lt;/p&gt;

&lt;p&gt;Para ver éstas métricas es necesario agregar el atributo &lt;code&gt;[MemoryAnaliser]&lt;/code&gt; a nuestra clase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;BenchmarkRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StringVsSpan&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MemoryDiagnoser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StringVsSpan&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;ListOfStrings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;concatStrings_concat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;retValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ListOfStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
            &lt;span class="n"&gt;retValue&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;ListOfStrings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;retValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;concatStrings_StringBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StringBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ListOfStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
            &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ListOfStrings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;concatStrings_Join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ListOfStrings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;GlobalSetup&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Startup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;faker&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;ListOfStrings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lorem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Paragraphs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"\n\n"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n\n"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lo ejecutamos ..&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KG4oiFlb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d8rslnoacdr3g9b62k80.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KG4oiFlb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d8rslnoacdr3g9b62k80.JPG" alt="Image description" width="796" height="61"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;... y obtenemos el siguiente resultado&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ynM9YE8r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nx086k5qdc2kw1c2b0yg.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ynM9YE8r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nx086k5qdc2kw1c2b0yg.JPG" alt="Image description" width="800" height="108"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Se ven 2 nuevas columnas, &lt;em&gt;Gen0&lt;/em&gt; y &lt;em&gt;Allocation&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Allocation&lt;/strong&gt;&lt;br&gt;
Es la cantidad de memoria en el Heap utilizada para correr la función que estamos probando por cada iteración&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gen0&lt;/strong&gt;/&lt;strong&gt;Gen1&lt;/strong&gt;/&lt;strong&gt;Gen2&lt;/strong&gt;&lt;br&gt;
En donde &lt;em&gt;Gen0&lt;/em&gt; es la ejecución del &lt;em&gt;GC&lt;/em&gt; donde recolecta objetos de corta duración y se ejecuta de forma mas periódica. En este caso no tenemos &lt;em&gt;Gen1&lt;/em&gt; ni &lt;em&gt;Gen2&lt;/em&gt;, pero estas ejecuciones del &lt;em&gt;GC&lt;/em&gt; recolectan objetos mas "pesados" y que tienen un ciclo de vida más largos.&lt;/p&gt;

&lt;p&gt;En nuestro resultado podemos concluir que la manera más eficiente de concatenar strings (dentro de las alternativas que se evaluaron) es utilizando &lt;code&gt;String.Join&lt;/code&gt; porque genera menos ejecuciones del &lt;em&gt;GC&lt;/em&gt; de primer nivel y utiliza menos alocación de memoria en el Heap.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusión
&lt;/h1&gt;

&lt;p&gt;Agregando el análisis de memoria a nuestros tests de &lt;em&gt;benchmark&lt;/em&gt; podemos identificar como se comporta, en manera general, diferentes estrategias para obtener un mismo resultado y darnos major información para elegir una y otra.&lt;/p&gt;

</description>
      <category>benchmarkdotnet</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>performance</category>
    </item>
    <item>
      <title>Midiendo el rendimiento de nuestro código con BenchmarkDotNet</title>
      <dc:creator>Andres Lozada Mosto</dc:creator>
      <pubDate>Sun, 16 Jul 2023 15:34:56 +0000</pubDate>
      <link>https://dev.to/andreslozadamosto/midiendo-el-rendimiento-de-nuestro-codigo-con-benchmarkdotnet-4ek4</link>
      <guid>https://dev.to/andreslozadamosto/midiendo-el-rendimiento-de-nuestro-codigo-con-benchmarkdotnet-4ek4</guid>
      <description>&lt;p&gt;Un &lt;em&gt;Benchmark&lt;/em&gt; (en Software) es un proceso que nos permite medir el rendimiento de un software, componente, algoritmo o bloque de código y verificar si cumple con los requerimientos establecidos de por ejemplo, uso de memoria o tiempos de ejecución.&lt;/p&gt;

&lt;p&gt;Este tipo de pruebas nos permite incluso comparar 2 componentes, 2 versiones de un mismo componente o diferentes algoritmos y verificar cual tiene mejor rendimiento.&lt;/p&gt;

&lt;h1&gt;
  
  
  Introducción a BenchmarkDotNet
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/dotnet/BenchmarkDotNet"&gt;BenchmarkDotNet&lt;/a&gt; es la librería de .Net mas aceptada por la comunidad siendo utilizada por varios proyectos de Microsoft.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;BenchmarkDotNet&lt;/em&gt; nos permite generar pruebas de &lt;em&gt;Benchmark&lt;/em&gt; y ejecutarlas con la misma simplicidad de ejecución que las de testing unitario.&lt;/p&gt;

&lt;p&gt;Entre las funcionalidades que podemos encontrar se encuentran:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ejecución de multiples Benchmark&lt;/li&gt;
&lt;li&gt;Setup/Cleanup entre ejecuciones&lt;/li&gt;
&lt;li&gt;Parametrización (al igual que por ejemplo XUnit/NUnit)&lt;/li&gt;
&lt;li&gt;Benchamark sobre diferentes plataformas/runtimes: NetFramework, Dotnet, WebAssembly, x86/x64, Mono, AOT&lt;/li&gt;
&lt;li&gt;Diagnosers: de memoria, JIT, Disasembly entre otros&lt;/li&gt;
&lt;li&gt;Exporters: CSV, HTML, RPlot, JSON, Markdown, etc&lt;/li&gt;
&lt;li&gt;Generación de trace files para su posterior análisis en PerfView/Visual Studio Profiler/SpeedScope&lt;/li&gt;
&lt;li&gt;Entre otros&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Creando nuestro primer Benchmark
&lt;/h1&gt;

&lt;p&gt;Nuestro &lt;em&gt;Benchmark&lt;/em&gt; debe esta encapsulado en una clase y por cada función atómica de código a analizar le debemos agregar el atributo &lt;code&gt;[Benchmark]&lt;/code&gt; para que la librería pueda auto-descubrir los bloques de código a ejecutar.&lt;/p&gt;

&lt;p&gt;Usaremos &lt;a href="https://github.com/bchavez/Bogus"&gt;Bogus&lt;/a&gt; para generar Fake data y el atributo &lt;code&gt;[GlobalSetup]&lt;/code&gt;para generar una función de inicialización donde inicializamos lo que necesitamos. Ésta función se ejecutará una única vez y no afectará las métricas.&lt;/p&gt;

&lt;p&gt;Vamos al ejemplo básico&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;BenchmarkDotNet.Attributes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;BenchmarkDotNet.Running&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Bogus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;BenchmarkRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BenchmarkFor&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BenchmarkFor&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;__amountOfItems&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;ListOfStrings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Baseline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;ForClassic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;retValues&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;__amountOfItems&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ListOfStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt; 
            &lt;span class="n"&gt;retValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ListOfStrings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;retValues&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;ForWithLinQ&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ListOfStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;GlobalSetup&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Startup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;faker&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;ListOfStrings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Enumerable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;__amountOfItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lorem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Word&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hay que prestar atención que cuando se realizan estas pruebas el entorno donde se ejecutan es muy importante porque puede afectar los tiempos de gran manera. Es recomendable ejecutar el proceso desde la consola (para no attachar el debugger), compilar en modo release y no tener otros procesos corriendo en la notebook y si se puede ejecutar en un entorno aislado (ej en una instancia en cloud), mucho mejor.&lt;/p&gt;

&lt;p&gt;Ahora sí, lo ejecutamos...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--haHOyCkQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mbgwv9r8qxyh8h8iwdwk.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--haHOyCkQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mbgwv9r8qxyh8h8iwdwk.JPG" alt="Image description" width="796" height="61"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;... y luego de esperar un ratito (si, tarda y genera mucho log por pantalla) obtenemos la siguiente información&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FDZbxqaT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zcjd2hxoanxedm5gugut.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FDZbxqaT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zcjd2hxoanxedm5gugut.JPG" alt="Image description" width="683" height="747"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lo más importante es el último recuadro en donde vemos los tiempos promedio que tardo cada función que marcamos como &lt;code&gt;[Benchmark]&lt;/code&gt;,  siendo la primera, 2x veces más rápida que la opción que utiliza &lt;code&gt;Linq&lt;/code&gt;. En la misma, el resto de las columnas muestran la desviación estándar, la mediana y el ratio.&lt;/p&gt;

&lt;p&gt;Algo importante a considerar en algunos casos es el &lt;em&gt;Histograma&lt;/em&gt; que muestra para cada función. En ellos podemos ver como se distribuyeron los tiempos de las sucesivas ejecuciones y ante ciertos casos no ayudará a encontrar puntos de mejora.&lt;/p&gt;

&lt;p&gt;Por último, se muestra la sección de &lt;em&gt;Outliers&lt;/em&gt; que nos indica cuantos casos fueron eliminados de la muestra porque estuvieron muy por fuera del rango del resto de los casos (ya sea que estuvieron muy por debajo o muy por arriba).&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusión
&lt;/h1&gt;

&lt;p&gt;Conocer como realizar un &lt;em&gt;Benchmark&lt;/em&gt; de nuestro código es una habilidad muy útil para tenerla dentro de nuestro &lt;em&gt;toolbox&lt;/em&gt; de herramientas como desarrollador y nos va a ser de mucha ayuda cuando queramos medir la performance de nuestro código.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>benchmarkdot</category>
      <category>csharp</category>
      <category>programming</category>
    </item>
    <item>
      <title>Creando un API en Net Core - Creando un cliente para nuestro API</title>
      <dc:creator>Andres Lozada Mosto</dc:creator>
      <pubDate>Fri, 09 Apr 2021 22:28:26 +0000</pubDate>
      <link>https://dev.to/andreslozadamosto/creando-un-api-en-net-core-5-creando-un-cliente-para-nuestro-api-1mb3</link>
      <guid>https://dev.to/andreslozadamosto/creando-un-api-en-net-core-5-creando-un-cliente-para-nuestro-api-1mb3</guid>
      <description>&lt;p&gt;En el artículo &lt;a href="https://dev.to/andreslozadamosto/creando-un-api-en-net-core-5-openapi-swagger-3fe3"&gt;anterior&lt;/a&gt; vimos como agregar &lt;em&gt;Swagger&lt;/em&gt; y &lt;em&gt;Open API&lt;/em&gt; junto con la librería &lt;em&gt;Swashbuckle&lt;/em&gt; en nuestros proyectos de &lt;strong&gt;APIs&lt;/strong&gt;. Es el momento de ver las opciones que tenemos para generar clientes que consuman nuestro &lt;strong&gt;API&lt;/strong&gt; desde diferentes lenguajes de programación.&lt;/p&gt;

&lt;p&gt;En primer lugar veremos como generar el archivo &lt;em&gt;JSON&lt;/em&gt; con la especificación de &lt;em&gt;Open API&lt;/em&gt;  de nuestro &lt;strong&gt;API&lt;/strong&gt; de forma que lo podamos guardar físicamente para su uso posterior, ya sea para generar los clientes como para utilizarlo en cualquier otro tool de &lt;strong&gt;APIs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Luego, con la ayuda de &lt;em&gt;NSwagStudio&lt;/em&gt; vamos a crear clientes en lenguajes &lt;em&gt;Typescript&lt;/em&gt; y &lt;em&gt;C#&lt;/em&gt; junto con un pequeño ejemplo de como utilizarlos.&lt;/p&gt;

&lt;p&gt;Por último, vamos a generar generar clientes en otros lenguajes como &lt;em&gt;Java&lt;/em&gt;, &lt;em&gt;NodeJS&lt;/em&gt; y &lt;em&gt;Python&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Les parece interesante? Vamos? 💪&lt;/p&gt;




&lt;h1&gt;
  
  
  Generando Open API JSON
&lt;/h1&gt;

&lt;p&gt;La librería &lt;em&gt;Swashbuckle&lt;/em&gt; nos provee de una &lt;em&gt;UI&lt;/em&gt; desde donde podemos obtener, entre otras grandes funcionalidades, el archivo &lt;em&gt;JSON&lt;/em&gt; con la documentación de &lt;em&gt;Open API&lt;/em&gt;. Esta forma no es la más útil si estamos trabajando en un entorno de integración continua y nuestros &lt;em&gt;"Clientes"&lt;/em&gt; se generan de manera automática.&lt;/p&gt;

&lt;p&gt;Por suerte &lt;em&gt;Swashbuckle&lt;/em&gt; nos provee un &lt;a href="https://github.com/domaindrivendev/Swashbuckle.AspNetCore#swashbuckleaspnetcorecli" rel="noopener noreferrer"&gt;tool&lt;/a&gt; que nos ayuda a generar de una manera muy simple y totalmente compatible con entornos de &lt;em&gt;CI&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Para ello, debemos instalar el &lt;em&gt;Global Tool&lt;/em&gt; posicionándonos en la carpeta del proyecto del &lt;strong&gt;API&lt;/strong&gt; desde la línea de comandos y escribir lo siguiente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--version&lt;/span&gt; 6.1.1 Swashbuckle.AspNetCore.Cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Recomiendo antes de realizar su instalación revisar la versión más nueva o bien la correspondiente a la que se debe utilizar de acuerdo a la versión de &lt;em&gt;Net Core&lt;/em&gt; utilizada).&lt;/p&gt;

&lt;p&gt;Una vez realizado el paso anterior podremos generar el archivo de la siguiente manera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet swagger tofile &lt;span class="nt"&gt;--output&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;output] &lt;span class="o"&gt;[&lt;/span&gt;startupassembly] &lt;span class="o"&gt;[&lt;/span&gt;swaggerdoc]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Donde&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;[output]&lt;/code&gt;: Es el &lt;em&gt;Path&lt;/em&gt; donde queremos guardar el archivo generado&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[startupassembly]&lt;/code&gt;: Es el &lt;em&gt;Path&lt;/em&gt; donde se ubica la &lt;em&gt;DLL&lt;/em&gt; con el archivo de &lt;code&gt;startup.cs&lt;/code&gt; del proyecto&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[swaggerdoc]&lt;/code&gt; Es el nombre que le dimos al documento &lt;em&gt;Swagger&lt;/em&gt; cuando lo incluimos en el &lt;code&gt;startup.cs&lt;/code&gt; (&lt;code&gt;c.SwaggerDoc("MyFirstNetCoreRESTAPI" &amp;lt;--- es este nombre&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En nuestro &lt;strong&gt;API&lt;/strong&gt; de ejemplo el comando queda de la siguiente forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet swagger tofile &lt;span class="nt"&gt;--output&lt;/span&gt; openapispec.json .&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="se"&gt;\D&lt;/span&gt;ebug&lt;span class="se"&gt;\n&lt;/span&gt;et5.0&lt;span class="se"&gt;\M&lt;/span&gt;yFirstNetCoreWebAPI.WebAPI.dll MyFirstNetCoreRESTAPI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos ver el output del comando&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi5sbh22761yr3htqnqm4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi5sbh22761yr3htqnqm4.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Primer paso completo, ya tenemos el archivo con la especificación de &lt;em&gt;Open API&lt;/em&gt; generado.&lt;/p&gt;


&lt;h1&gt;
  
  
  NSwagStudio
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/RicoSuter/NSwag/wiki/NSwagStudio" rel="noopener noreferrer"&gt;NSwagStudio&lt;/a&gt; es una herramienta de &lt;em&gt;NSwag&lt;/em&gt; para la creación de clientes en lenguajes &lt;em&gt;CSharp&lt;/em&gt; y &lt;em&gt;TypeScript&lt;/em&gt; (con opción a exportar listo para usar desde &lt;em&gt;Angular&lt;/em&gt;, &lt;em&gt;React&lt;/em&gt;, etc) de un &lt;strong&gt;API&lt;/strong&gt; a partir de un archivo en formato &lt;em&gt;Open API&lt;/em&gt; .&lt;/p&gt;

&lt;p&gt;Este programa nos permitirá guardar la configuración de la generación de los &lt;strong&gt;API&lt;/strong&gt; en un archivo &lt;em&gt;.nswag&lt;/em&gt; para poder utilizarlos todas las veces que sean necesarias.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4z649keyxnopnphev1n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4z649keyxnopnphev1n.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Podemos seleccionar desde donde queremos obtener la información del &lt;strong&gt;API&lt;/strong&gt;, ya sea desde un archivo físico, una URL, binarios, etc e indicar el Runtime de &lt;em&gt;.Net Core&lt;/em&gt; o &lt;em&gt;.Net Framework&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;La configuración para la creación del &lt;strong&gt;API&lt;/strong&gt; provee una gran cantidad de opciones la cual podremos ver en otro artículo.&lt;/p&gt;

&lt;p&gt;En nuestro proyecto de &lt;strong&gt;API&lt;/strong&gt; ejemplo (que pueden descargar desde &lt;a href="https://github.com/andreslozadamosto/MyFirstNetCoreAPI-Tutorial" rel="noopener noreferrer"&gt;Github&lt;/a&gt;) creamos 2 nuevos proyectos.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;MyFirstNetCoreWebAPI.WebAPI.Client&lt;/em&gt;: El cual es un proyecto de librería donde colocamos el archivo &lt;em&gt;.swag&lt;/em&gt; y donde se va a generar el archivo con el código del cliente&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;MyFirstNetCoreWebAPI.ConsoleClient&lt;/em&gt;: Es un proyecto de Consola para probar el cliente del &lt;strong&gt;API&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A gusto personal, prefiero que cada &lt;em&gt;controller&lt;/em&gt; del &lt;strong&gt;API&lt;/strong&gt; tenga su propia &lt;em&gt;Interfaz&lt;/em&gt; e &lt;em&gt;implementación&lt;/em&gt; donde su &lt;em&gt;constructor&lt;/em&gt; reciba como parámetro una instancia de  &lt;em&gt;HTTPClient&lt;/em&gt;. De esta manera podemos configurar el &lt;em&gt;HTTPClient&lt;/em&gt; de la manera que nosotros queramos, como por ejemplo agregarle un token de autenticación, políticas de retry (con Polly) y cualquier otra cosa que necesitemos de forma desacoplada del cliente.&lt;/p&gt;

&lt;p&gt;La próxima vez que se actualice un &lt;em&gt;endpoint&lt;/em&gt; del &lt;strong&gt;API&lt;/strong&gt; simplemente es necesario darle doble click al archivo &lt;em&gt;.nswag&lt;/em&gt; y generar nuevamente los archivos. Fácil no? 💪&lt;/p&gt;

&lt;p&gt;Con esta configuración tenemos un cliente de nuestro &lt;strong&gt;API&lt;/strong&gt; en una librería compartida que podremos utilizar desde cualquier proyecto de la solución o subirla a un repositorio de &lt;em&gt;Nuget&lt;/em&gt; para ser compartida entre diferentes proyectos.&lt;/p&gt;

&lt;p&gt;Lo mismo podemos hacer para los clientes de &lt;em&gt;TypeScript&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  NSwagStudio + CLI
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;NSwagStudio&lt;/em&gt; nos permite generar los clientes ejecutando por línea de comando programa a partir de un archivo &lt;em&gt;.nswag&lt;/em&gt; de la siguiente manera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nswag run /runtime:Net50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este comando, claro, lo debemos ejecutar sobre la misma ubicación donde se encuentre el archivo &lt;em&gt;.nswag&lt;/em&gt; y en la &lt;a href="https://github.com/RicoSuter/NSwag/wiki/CommandLine" rel="noopener noreferrer"&gt;documentación&lt;/a&gt; podemos ver todas las opciones que podemos pasarle.&lt;/p&gt;

&lt;p&gt;Esta opción nos puede ser útil para entornos de &lt;strong&gt;&lt;em&gt;CI&lt;/em&gt;&lt;/strong&gt; o bien para agregarlo al &lt;em&gt;PostBuild&lt;/em&gt; del &lt;em&gt;MSBuild&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  NSwagStudio + MSBuild
&lt;/h2&gt;

&lt;p&gt;La generación de los clientes también podemos agregarla como un Task del &lt;em&gt;MSBuild&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;En mi caso particular no es la opción que mas me gusta pero se que a muchos les es muy útil.&lt;/p&gt;

&lt;p&gt;Pueden ver toda la documentación &lt;a href="https://github.com/RicoSuter/NSwag/wiki/NSwag.MSBuild" rel="noopener noreferrer"&gt;aquí&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  NSwagStudio +  Otras alternativas
&lt;/h2&gt;

&lt;p&gt;En entornos de &lt;strong&gt;CI&lt;/strong&gt; podemos instalar &lt;em&gt;NSwagStudio&lt;/em&gt; por medio de &lt;em&gt;Chocolatey&lt;/em&gt; y ejecutarlo por medio del &lt;em&gt;CLI&lt;/em&gt; tal como vimos antes, pero la restricción que tenemos es que no queda tan "standard" para un &lt;em&gt;Pipeline&lt;/em&gt; de Build.&lt;/p&gt;

&lt;p&gt;La opción en estos casos podemos utilizar otras altenativas&lt;/p&gt;

&lt;h3&gt;
  
  
  NodeJS
&lt;/h3&gt;

&lt;p&gt;Podemos utilizar el &lt;a href="https://www.npmjs.com/package/nswag" rel="noopener noreferrer"&gt;npm package&lt;/a&gt; oficial para instalarlo en el &lt;strong&gt;CI&lt;/strong&gt;, &lt;strong&gt;Docker&lt;/strong&gt;  o desde donde sea necesario y ejecutarlo desde allí&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker
&lt;/h3&gt;

&lt;p&gt;Otra opción es utilizar &lt;strong&gt;Docker&lt;/strong&gt;, ya se para integrarlo en un stage del docker que compila la aplicación en el &lt;em&gt;Pipeline&lt;/em&gt; o en un paso separado. En este caso tenemos dos opciones, o bien utilizar la versión de &lt;em&gt;NodeJS&lt;/em&gt; como vimos recién o utilizar el ejecutable sobre &lt;em&gt;NetCore&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;En el proyecto  &lt;em&gt;MyFirstNetCoreWebAPI.ConsoleClient&lt;/em&gt; tenemos ahora un &lt;em&gt;Dockerfile&lt;/em&gt;, un archivo &lt;em&gt;ClientGenerator-docker-net.nswag&lt;/em&gt; específico para ejecutarse dentro de &lt;em&gt;Docker&lt;/em&gt; y un archivo de &lt;em&gt;Json&lt;/em&gt; de &lt;em&gt;OpenAPI&lt;/em&gt; con la especificación del &lt;strong&gt;API&lt;/strong&gt; que creamos en pasos previos.&lt;/p&gt;

&lt;p&gt;El ejemplo del &lt;em&gt;Dockerfile&lt;/em&gt; es muy simple y sirve de base para que armen uno propio basado en sus necesidades. &lt;/p&gt;

&lt;p&gt;El detalle de cada paso que realiza el &lt;em&gt;Dockerfile&lt;/em&gt; esta explicado en el mismo archivo.&lt;/p&gt;

&lt;p&gt;Veamos como lo ejecutamos&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;// creamos la imagen
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myfirstapi/nswag &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;// corremos un container con la imagen
docker run &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;pwd&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:/resultado myfirstapi/nswag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como pueden ver, en este último paso montamos el directorio actual en la carpeta &lt;em&gt;"resultado"&lt;/em&gt;. Si no vieron el &lt;em&gt;Dockerfile&lt;/em&gt; les cuento que el &lt;em&gt;entrypoint&lt;/em&gt; realiza la exportación de los archivos de los clientes a esta carpeta que tenemos montada y de esta manera sacamos los archivos generados fuera del container.&lt;/p&gt;

&lt;p&gt;Si quieren revisar como quedan los archivos, pueden ejecutar este comando para navegar el container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; /bin/bash &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;pwd&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:/resultado myfirstapi/nswag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  OpenAPi Tools
&lt;/h1&gt;

&lt;p&gt;La sección anterior vimos como generamos clientes para &lt;em&gt;TypeScript&lt;/em&gt; y &lt;em&gt;CSharp&lt;/em&gt;, pero que pasa si necesitamos para otros lenguajes como &lt;em&gt;Java&lt;/em&gt; o &lt;em&gt;Python&lt;/em&gt;? La respuesta es el generador oficial de &lt;em&gt;Open API&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Pueden revisar la cantidad infinita de generadores desde su web &lt;a href="https://openapi-generator.tech/" rel="noopener noreferrer"&gt;https://openapi-generator.tech/&lt;/a&gt; y lo mejor es que pueden utilizar la versión de &lt;em&gt;NodeJS&lt;/em&gt; o bien proveen un &lt;em&gt;Docker&lt;/em&gt; para no tener que configurar nada y tenerlo funcionando de forma inmediata.&lt;/p&gt;

&lt;p&gt;La verdad que iba a poner unos ejemplos, pero ya se hace muy largo el artículo. Dejen unos comentarios si quieren que el próximo artículo lo veamos 😁.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>api</category>
      <category>dotnet</category>
      <category>rest</category>
    </item>
    <item>
      <title>Creando un API en Net Core - OpenAPI &amp; Swagger</title>
      <dc:creator>Andres Lozada Mosto</dc:creator>
      <pubDate>Fri, 26 Feb 2021 03:24:44 +0000</pubDate>
      <link>https://dev.to/andreslozadamosto/creando-un-api-en-net-core-5-openapi-swagger-3fe3</link>
      <guid>https://dev.to/andreslozadamosto/creando-un-api-en-net-core-5-openapi-swagger-3fe3</guid>
      <description>&lt;p&gt;Hi there! 👋&lt;/p&gt;

&lt;p&gt;Llegó una nueva parte de esta guía de creación de &lt;strong&gt;APIs&lt;/strong&gt; en &lt;strong&gt;&lt;em&gt;NetCore&lt;/em&gt;&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;En este artículo veremos cómo incluir &lt;em&gt;OpenAPI&lt;/em&gt; en nuestro proyecto.&lt;/p&gt;

&lt;p&gt;Si quieren ver el resultado final ejecutándose, pueden bajarlo del &lt;a href="https://github.com/andreslozadamosto/MyFirstNetCoreAPI-Tutorial" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos? &lt;/p&gt;




&lt;h1&gt;
  
  
  ¿Qué es OpenAPI?
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;OpenAPI&lt;/em&gt; (que ya fue mencionada en nuestro &lt;a href="https://dev.to/andreslozadamosto/creando-un-api-en-net-core-5-rest-api-3e2l"&gt;segundo&lt;/a&gt; artículo) es un estándar que predefine como describir o documentar nuestro &lt;strong&gt;API&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Dejando detalles de lado, &lt;em&gt;OpenAPI&lt;/em&gt; define debe existir un archivo en formato &lt;em&gt;JSON&lt;/em&gt; o &lt;em&gt;YAML&lt;/em&gt; en donde se definan todos lo recursos del API, acciones que se le pueden realizar, información que se debe enviar a cada endpoint y que se puede recibir, etc.&lt;/p&gt;

&lt;p&gt;En definitiva, regula todo el aspecto de como escribir ese archivo y por consiguiente, define la forma de documentar nuestro &lt;strong&gt;API&lt;/strong&gt; para que otro equipo/persona sepa cómo utilizarlo.&lt;/p&gt;

&lt;h1&gt;
  
  
  ¿Qué es Swagger?
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Swagger&lt;/em&gt; es un proyecto open source que implementa el estándar. El proyecto fue cedido a la Open API Initiative y desde ese momento se transforman en sinónimos.&lt;/p&gt;

&lt;p&gt;Hay que diferenciar que uno es el estándar y el otro (Swagger) es una implementación y creación del UI en base al archivo &lt;em&gt;JSON&lt;/em&gt;/&lt;em&gt;YAML&lt;/em&gt; generado.&lt;/p&gt;

&lt;h1&gt;
  
  
  Alternativas
&lt;/h1&gt;

&lt;p&gt;En cuanto a especificaciones, tenemos a &lt;em&gt;&lt;a href="https://raml.org/" rel="noopener noreferrer"&gt;RAML&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Para reemplazar el UI, tenemos alternativas en donde se destaca &lt;em&gt;&lt;a href="https://redoc.ly/" rel="noopener noreferrer"&gt;Redoc&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;En este &lt;a href="https://nordicapis.com/ultimate-guide-to-30-api-documentation-solutions" rel="noopener noreferrer"&gt;link&lt;/a&gt; podemos ver un listado de opciones al UI de Swagger.&lt;/p&gt;




&lt;h1&gt;
  
  
  Agregar OpenAPI y Swagger UI a nuestro API
&lt;/h1&gt;

&lt;p&gt;En mundo de .Net Core hay 2 librerías principales, &lt;a href="https://github.com/domaindrivendev/Swashbuckle.AspNetCore" rel="noopener noreferrer"&gt;Swashbuckle&lt;/a&gt; y &lt;a href="https://github.com/RicoSuter/NSwag" rel="noopener noreferrer"&gt;NSwag&lt;/a&gt;. En este caso vamos a utilizar &lt;em&gt;Swashbuckle&lt;/em&gt; que es la que desde &lt;em&gt;.Net 5&lt;/em&gt; viene incluida por defecto en el template de &lt;em&gt;APIs&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Librerías
&lt;/h2&gt;

&lt;p&gt;Agregamos el siguiente paquete de Nuget&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; dotnet add package Swashbuckle.AspNetCore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configurando el Startup.cs
&lt;/h2&gt;

&lt;p&gt;Debemos agregar unas líneas en las funciones &lt;code&gt;ConfigurationServices&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSwaggerGen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y en &lt;code&gt;Configure&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSwagger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSwaggerUI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SwaggerEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/swagger/v1/swagger.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"My API V1"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Customizaciones
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;SwaggerUIPodemos agregar información a la documentación, agregar nuestros propios archivos _css&lt;/em&gt; y &lt;em&gt;javascript&lt;/em&gt; para personalizar la pantalla.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentación de XMLDocs
&lt;/h3&gt;

&lt;p&gt;Podemos utilizar nuestra documentación de código por medio de XML Docs para mostrar información en el UI.&lt;/p&gt;

&lt;p&gt;Primero debemos indicarle al proyecto que genere el archivo XML con los comentarios de las funciones&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;GenerateDocumentationFile&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/GenerateDocumentationFile&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;NoWarn&amp;gt;&lt;/span&gt;$(NoWarn);1591&lt;span class="nt"&gt;&amp;lt;/NoWarn&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Luego le decimos a &lt;em&gt;Swagger&lt;/em&gt; que cargue el archivo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSwaggerGen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SwaggerDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;OpenApiInfo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"My First Net Core REST API"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"v1"&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;xmlFile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Assembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetExecutingAssembly&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;GetName&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.xml"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;xmlPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseDirectory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xmlFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IncludeXmlComments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xmlPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F393jerntvjfeuzu56uyn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F393jerntvjfeuzu56uyn.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Logo customizado
&lt;/h3&gt;

&lt;p&gt;Podemos agregar customizaciones a la UI para que se adapte a la gráfica de nuestra empresa. Para eso podemos agregar archivos &lt;em&gt;CSS&lt;/em&gt;, &lt;em&gt;Javascript&lt;/em&gt; o bien utilizar un &lt;em&gt;HTML&lt;/em&gt; completo personalizado.&lt;/p&gt;

&lt;p&gt;Para los cambios básicos (cambio de logo y color del header) solo necesitaremos agregar un &lt;em&gt;CSS&lt;/em&gt; nuevo de la siguiente manera&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.UseStaticFiles();
app.UseSwaggerUI(c =&amp;gt;
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "MyFirstNetCoreWebAPI.WebAPI v1");
    c.InjectStylesheet("/swagger-ui/swagger-custom.css");
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;y el archivo css dentro de una nueva carpeta que llamaremos &lt;code&gt;wwwroot&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.swagger-ui&lt;/span&gt; &lt;span class="nc"&gt;.topbar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1abc9c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.topbar-wrapper&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"Swagger UI"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.topbar-wrapper&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;-moz-box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url(apilogo.png)&lt;/span&gt; &lt;span class="nb"&gt;no-repeat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Width of new image */&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;86px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Height of new image */&lt;/span&gt;
    &lt;span class="nl"&gt;padding-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;180px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Equal to width of new image */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nc"&gt;.topbar-wrapper&lt;/span&gt; &lt;span class="nc"&gt;.select-label&lt;/span&gt; &lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#212121&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El previo &lt;em&gt;css&lt;/em&gt; nos provee este bonito resultado.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixxelk8s8iaa2wjsql6p.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixxelk8s8iaa2wjsql6p.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Información de licencia y autoría
&lt;/h3&gt;

&lt;p&gt;Otra cosa que podemos agregar e importante cuando la &lt;strong&gt;API&lt;/strong&gt; es pública es la información de Autoría y Licencia.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSwaggerGen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SwaggerDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;OpenApiInfo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"My First Net Core REST API"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"A simple example ASP.NET Core Web API by Andrés Lozada Mosto, You can find this tutorial here: https://dev.to/andreslozadamosto/creando-un-api-en-net-core-5-intro-2nc2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;TermsOfService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://example.com/terms"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Contact&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;OpenApiContact&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Andres Lozada Mosto"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://github.com/andreslozadamosto"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;License&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;OpenApiLicense&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Use under MIT licence"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://choosealicense.com/licenses/mit/"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Definiendo datos requeridos
&lt;/h3&gt;

&lt;p&gt;Podemos utilizar los atributos como &lt;em&gt;[required]&lt;/em&gt; para indicar cuáles datos son requeridos por el objeto recibido como parámetro&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74iegwpbwo1bl127unl0.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74iegwpbwo1bl127unl0.JPG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Tipo de salida
&lt;/h3&gt;

&lt;p&gt;Algo muy importante es indicar al público target que utilizará nuestro &lt;strong&gt;API&lt;/strong&gt; el formato en que recibirá las respuestas. Esto se realiza indicando el &lt;em&gt;MIME TYPE&lt;/em&gt; devuelto por el &lt;em&gt;Action&lt;/em&gt; del &lt;em&gt;Controller&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Produces&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este atributo lo podemos utilizar a nivel de Clase como de Action Method.&lt;/p&gt;

&lt;p&gt;Como veremos en artículos posteriores, cuando se llama a un &lt;strong&gt;API&lt;/strong&gt; desde un frontend en javascript éste le puede indicar que formato de tipo de respuesta le gustaría recibirf_JSON_, &lt;em&gt;XML&lt;/em&gt;, otro). &lt;strong&gt;&lt;em&gt;Net Core&lt;/em&gt;&lt;/strong&gt; provee soporte a esta funcionalidad y se pueden registrar cualquier tipo de formarteadores de salida. Pero este tema lo veremos en mayor profundidad en próximos artículos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Códigos de HTTP de respuesta [ProducesResponseType]
&lt;/h3&gt;

&lt;p&gt;Otra información muy relevante por los usuarios de nuestra &lt;strong&gt;API&lt;/strong&gt; es conocer cuales son los códigos HTTP de respuesta que podemos obtener al realizar un request.&lt;/p&gt;

&lt;p&gt;Para documentar esto utilizamos el siguiente atributo a nivel de Action Method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ProducesResponseType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status400BadRequest&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ProducesResponseType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status409Conflict&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ProducesResponseType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status201Created&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El enum &lt;em&gt;StatusCodes&lt;/em&gt; tiene enumerados para todos los diferentes códigos de respuesta HTTP.&lt;/p&gt;

&lt;p&gt;En el UI queda desglosado las diferentes posibles respuestas&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvr38qt2l4idmm73pb734.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvr38qt2l4idmm73pb734.JPG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Extra
&lt;/h1&gt;

&lt;p&gt;Hay un mundo de librerías que podemos utilizar junto con &lt;em&gt;Open API&lt;/em&gt; tales como generadores de librerías cliente (para consumir nuestro &lt;strong&gt;API&lt;/strong&gt; desde cualquier lenguaje), generadores de server stubs, chequeo de seguridad entre otros.&lt;/p&gt;

&lt;p&gt;Esta web &lt;a href="https://openapi.tools/" rel="noopener noreferrer"&gt;https://openapi.tools&lt;/a&gt; nos provee un gran listado de herramientas organizadas por tema.&lt;/p&gt;




</description>
      <category>csharp</category>
      <category>api</category>
      <category>dotnet</category>
      <category>rest</category>
    </item>
    <item>
      <title>Creando un API en Net Core - Mi primer API</title>
      <dc:creator>Andres Lozada Mosto</dc:creator>
      <pubDate>Wed, 03 Feb 2021 21:42:28 +0000</pubDate>
      <link>https://dev.to/andreslozadamosto/creando-un-api-en-net-core-5-mi-primer-api-319f</link>
      <guid>https://dev.to/andreslozadamosto/creando-un-api-en-net-core-5-mi-primer-api-319f</guid>
      <description>&lt;p&gt;Llegó el tan ansiado momento de meter mano en el código. Durante este artículo vamos a crear nuestro primer &lt;strong&gt;API&lt;/strong&gt; e implementar algunas de las cosas que vimos en los artículos anteriores.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pueden clonarse el proyecto desde &lt;a href="https://github.com/andreslozadamosto/MyFirstNetCoreAPI-Tutorial" rel="noopener noreferrer"&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Creando nuestro primer proyecto
&lt;/h1&gt;

&lt;p&gt;Si estamos utilizando un &lt;em&gt;IDE&lt;/em&gt; como &lt;em&gt;Visual Studio&lt;/em&gt; o &lt;em&gt;Rider&lt;/em&gt; simplemente vamos a la opción de crear un nuevo proyecto, seleccionamos el tipo de proyecto &lt;strong&gt;Web API&lt;/strong&gt; y seguimos las instrucciones. Con esta acción se generará un nuevo proyecto utilizando como base el template que viene integrado en &lt;strong&gt;&lt;em&gt;Net Core&lt;/em&gt;&lt;/strong&gt; y seleccionará la versión de &lt;strong&gt;&lt;em&gt;Net Core&lt;/em&gt;&lt;/strong&gt; más nueva que se encuentre instalada en el sistema (recomiendo utilizar la última, &lt;strong&gt;&lt;em&gt;Net Core 5&lt;/em&gt;&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;En cambio si estamos usando la línea de comandos y un editor como &lt;em&gt;VS Code&lt;/em&gt; para programar, debemos seguir estos pasos para crear la estructura del proyecto&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;// creamos la carpeta de la solución
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;MyFirstNetCoreWebAPI

// creamos el archivo de solución
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; dotnet new sln &lt;span class="nt"&gt;--name&lt;/span&gt; MyFirstNetCoreWebAPI

// creamos la carpeta de código
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;src
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;src

// creamos el proyecto de web api
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; dotnet new webapi &lt;span class="nt"&gt;--name&lt;/span&gt; MyFirstNetCoreWebAPI.WebAPI

// agregamos el proyecto a la solución
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; dotnet sln .&lt;span class="se"&gt;\M&lt;/span&gt;yFirstNetCoreWebAPI.sln add .&lt;span class="se"&gt;\s&lt;/span&gt;rc&lt;span class="se"&gt;\M&lt;/span&gt;yFirstNetCoreWebAPI.WebAPI&lt;span class="se"&gt;\M&lt;/span&gt;yFirstNetCoreWebAPI.WebAPI.csproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si nos gusta trabajar con la consola y/o queremos utilizar un &lt;em&gt;IDE&lt;/em&gt; liviano como &lt;em&gt;VS Code&lt;/em&gt; talvez convenga instalar el generador de &lt;strong&gt;dotnet&lt;/strong&gt; que nos ayudará a crear los elementos necesarios (mas allá de los &lt;a href="https://code.visualstudio.com/docs/languages/csharp" rel="noopener noreferrer"&gt;plugins&lt;/a&gt; de &lt;em&gt;VS Code&lt;/em&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; dotnet-aspnet-codegenerator

&lt;span class="c"&gt;## Agregamos el paquete al proyecto&lt;/span&gt;
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ya sea que creemos desde donde creemos el proyecto, luego éste puede utilizarse desde cualquier &lt;em&gt;IDE&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Con el proyecto creado, borramos las 2 clases que se crean de forma automática: &lt;code&gt;WeatherForecast.cs&lt;/code&gt; y &lt;code&gt;WeatherForecastController.cs&lt;/code&gt;. Es código del template que se agrega únicamente como referencia pero que nosotros no vamos a utilizar.&lt;/p&gt;

&lt;p&gt;La estructura base que les debe quedar es la siguiente:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Foompm3sqsfgfls3pcpv7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Foompm3sqsfgfls3pcpv7.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para los que son nuevos en &lt;strong&gt;&lt;em&gt;Net Core&lt;/em&gt;&lt;/strong&gt; vamos a repasar qué es cada cosa:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Program.cs&lt;/code&gt;: es el archivo que crea y levanta el Host donde se va a ejecutar el web api&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Startup.cs&lt;/code&gt;: es la clase de inicialización de la aplicación. En ella se realiza la configuración del servicio/app/web como por ejemplo la configuración de seguridad, de logging, de &lt;em&gt;OpenAPI&lt;/em&gt;/&lt;em&gt;Swagger&lt;/em&gt;, motor de inyección de dependencias (IOC), etc&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;appsettings.json&lt;/code&gt;: es un archivo de configuración externo y variable por ambiente&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Controllers&lt;/code&gt;: esta carpeta estarán los controllers que son los puntos de entrada de las URIs/endpoints del &lt;strong&gt;API&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Nuestro primer recurso
&lt;/h2&gt;

&lt;p&gt;Para nuestro ejemplo vamos a utilizar el recurso &lt;code&gt;usuarios&lt;/code&gt; en donde vamos a poder listar los usuarios existentes, consultar por un usuario en particular y agregar un nuevo usuario. &lt;/p&gt;

&lt;p&gt;Lo primero que debemos realizar es crear una clase controller que se llame &lt;code&gt;UsersController&lt;/code&gt; ya sea con el &lt;em&gt;IDE&lt;/em&gt; o con el generador de código.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; dotnet aspnet-codegenerator controller &lt;span class="nt"&gt;-api&lt;/span&gt; &lt;span class="nt"&gt;-async&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; UsersController &lt;span class="nt"&gt;-outDir&lt;/span&gt; Controllers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si ahora vemos el file system o el explorador del &lt;em&gt;IDE&lt;/em&gt; vemos que se creó un archivo con la estructura básica de un &lt;code&gt;Controller&lt;/code&gt; de &lt;strong&gt;Web API&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Dentro del controller comenzamos a agregar la lógica necesaria.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Listado de todos los usuarios&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;HttpGet&lt;span class="o"&gt;()]&lt;/span&gt;
public ActionResult&amp;lt;List&amp;lt;UserDto&amp;gt;&amp;gt; GetAllUsers&lt;span class="o"&gt;()&lt;/span&gt; 
    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Ok&lt;span class="o"&gt;(&lt;/span&gt;_userRepository
        .GetAll&lt;span class="o"&gt;()&lt;/span&gt;
        .Select&lt;span class="o"&gt;(&lt;/span&gt;x &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; UserDto.FromModel&lt;span class="o"&gt;(&lt;/span&gt;x&lt;span class="o"&gt;))&lt;/span&gt;.ToList&lt;span class="o"&gt;())&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Obtener un usuario en particular&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;HttpGet&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"{name}"&lt;/span&gt;&lt;span class="o"&gt;)]&lt;/span&gt;
public ActionResult&amp;lt;UserDto&amp;gt; GetUser&lt;span class="o"&gt;(&lt;/span&gt;string name&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    var user &lt;span class="o"&gt;=&lt;/span&gt; _userRepository.Get&lt;span class="o"&gt;(&lt;/span&gt;name&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;user &lt;span class="o"&gt;==&lt;/span&gt; null&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;Problem&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="s2"&gt;"User not found"&lt;/span&gt;,
            HttpContext.Request.Path,
            StatusCodes.Status404NotFound,
            &lt;span class="s2"&gt;"Bad parameters"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;Ok&lt;span class="o"&gt;(&lt;/span&gt;UserDto.FromModel&lt;span class="o"&gt;(&lt;/span&gt;user&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Crear un nuevo usuario&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;HttpPost&lt;span class="o"&gt;()]&lt;/span&gt;
public ActionResult&amp;lt;UserDto&amp;gt; AddUser&lt;span class="o"&gt;([&lt;/span&gt;FromBody] UserDto newUserToCreate&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;string.IsNullOrEmpty&lt;span class="o"&gt;(&lt;/span&gt;newUserToCreate.Name&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;BadRequest&lt;span class="o"&gt;(&lt;/span&gt;new ProblemDetails&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
            Title &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Bad parameters"&lt;/span&gt;,
            Detail &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The name of the new user cannot be empty or null"&lt;/span&gt;,
            Instance &lt;span class="o"&gt;=&lt;/span&gt; HttpContext.Request.Path
        &lt;span class="o"&gt;})&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    var userAlreadyExists &lt;span class="o"&gt;=&lt;/span&gt; _userRepository.Exists&lt;span class="o"&gt;(&lt;/span&gt;newUserToCreate.Name&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;userAlreadyExists&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;Conflict&lt;span class="o"&gt;(&lt;/span&gt;new ProblemDetails&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
            Detail &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"User already exists"&lt;/span&gt;,
            Title &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Bad parameters"&lt;/span&gt;,
            Instance &lt;span class="o"&gt;=&lt;/span&gt; HttpContext.Request.Path
        &lt;span class="o"&gt;})&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    var newUser &lt;span class="o"&gt;=&lt;/span&gt; new User&lt;span class="o"&gt;(&lt;/span&gt;newUserToCreate.Name&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    _userRepository.Add&lt;span class="o"&gt;(&lt;/span&gt;newUser&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;Created&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;$"/users/{newUserToCreate.Name}"&lt;/span&gt;, UserDto.FromModel&lt;span class="o"&gt;(&lt;/span&gt;newUser&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Es un ejemplo simple y bastante trivial de una *&lt;em&gt;API&lt;/em&gt;, pero podemos ver varios de lo visto en los artículos anteriores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;URI de recurso&lt;/strong&gt;: el recurso &lt;em&gt;usuario&lt;/em&gt; tiene su URI de acceso &lt;a href="http://domain.com/api/users" rel="noopener noreferrer"&gt;http://domain.com/api/users&lt;/a&gt;. Esto lo provee el &lt;em&gt;Atributte&lt;/em&gt; &lt;code&gt;[Route("api/[controller]")]&lt;/code&gt; que se encuentra al inicio de la clase. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Verbs&lt;/strong&gt;: cada acción sobre el recurso está asociado al &lt;em&gt;verbo HTTP&lt;/em&gt; correcto. Los que son de consulta utilizamos &lt;em&gt;GET&lt;/em&gt; y el que es de creación &lt;em&gt;POST&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safe&lt;/strong&gt;: las acciones sobre el recurso utilizando el verbo &lt;em&gt;GET&lt;/em&gt; no genera efectos secundarios sobre el recurso&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotencia&lt;/strong&gt;: Los Get siempre devuelven la misma información ante múltiples peticiones iguales.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Códigos de respuesta&lt;/strong&gt;: Utilizamos los correctos para cada caso, &lt;em&gt;200&lt;/em&gt; para request satisfactorio devolviendo los datos solicitados, &lt;em&gt;201&lt;/em&gt; para cuando se crea un nuevo recurso, &lt;em&gt;404&lt;/em&gt; para cuando no es encontrado el recurso solicitado y &lt;em&gt;400&lt;/em&gt; si los datos que vinieron en el request tienen algún problema.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open API&lt;/strong&gt;: el scaffold del template ya viene con la implementación básica de &lt;em&gt;OpenAPI&lt;/em&gt;/&lt;em&gt;Swagger&lt;/em&gt; por lo que la documentación está disponible al ejecutar la aplicación.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aquí podemos ver la clase completa&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/[controller]"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IUserRepository&lt;/span&gt; &lt;span class="n"&gt;_userRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;UsersController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IUserRepository&lt;/span&gt; &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_userRepository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;ActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAllUsers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_userRepository&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;UserDto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{name}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;ActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_userRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Problem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"User not found"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status404NotFound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Bad parameters"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserDto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;ActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;AddUser&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;FromBody&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;UserDto&lt;/span&gt; &lt;span class="n"&gt;newUserToCreate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newUserToCreate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ProblemDetails&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bad parameters"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Detail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"The name of the new user cannot be empty or null"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userAlreadyExists&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_userRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newUserToCreate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userAlreadyExists&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Conflict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ProblemDetails&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Detail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"User already exists"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bad parameters"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;newUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newUserToCreate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;_userRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Created&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"/users/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;newUserToCreate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UserDto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Problem Details
&lt;/h2&gt;

&lt;p&gt;En el API otra cosa que podemos ver es la utilización del estándar de &lt;a href="https://tools.ietf.org/html/rfc7807#:~:text=RFC%207807%20Problem%20Details%20March%202016%20Finally%2C%20an%20application%20might,replace%20existing%20domain%2Dspecific%20formats" rel="noopener noreferrer"&gt;ProblemDetails`&lt;/a&gt; para notificar el error producido en la ejecución del request.&lt;/p&gt;

&lt;p&gt;Esta manera de comunicar que fue lo sucedido en el &lt;strong&gt;API&lt;/strong&gt; estamos convalidando el principio de &lt;em&gt;Interfaz uniforme&lt;/em&gt; que mencionamos anteriormente. &lt;/p&gt;

&lt;p&gt;Lo que define este estándar es como un &lt;strong&gt;HTTP API&lt;/strong&gt; debe informar lo sucedido en la aplicación cuando se devuelve un código de error &lt;em&gt;3xx&lt;/em&gt;, &lt;em&gt;4xx&lt;/em&gt; o &lt;em&gt;5xx&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Por suerte para nosotros &lt;strong&gt;&lt;em&gt;Net Core&lt;/em&gt;&lt;/strong&gt; resuelve este tema de manera automática para las excepciones no controladas, parámetros vacíos y demás controles que realiza internamente antes de ejecutar nuestro código del controller, quedando de nuestro lado implementarlo en nuestros métodos.&lt;/p&gt;

&lt;p&gt;En el ejemplo del &lt;code&gt;Controller&lt;/code&gt;, vemos que hay 2 maneras de implementarlo, una es llamando al método &lt;code&gt;Problems&lt;/code&gt; y el 2do es llamado al método del &lt;strong&gt;HTTP Status&lt;/strong&gt; que queremos retornar y pasándole como parámetro una instancia de la clase &lt;code&gt;ProblemDetails&lt;/code&gt;. Ambos métodos son igualmente válidos.&lt;/p&gt;

&lt;p&gt;Del código también se desprende que no devolvemos directamente el objeto User sino que lo mapeamos a una clase &lt;code&gt;UserDto&lt;/code&gt;. Esto es así porque por lo general obtendremos el usuario de una base de datos por medio de Entidades de Negocio. Nunca es una buena práctica  devolver una &lt;em&gt;Entidad de Negocio&lt;/em&gt; al &lt;em&gt;Frontend&lt;/em&gt;, sino devolver un objeto que defina un contrato entre el &lt;strong&gt;API&lt;/strong&gt; y el cliente que realiza el request sin que esté acoplado al negocio.&lt;/p&gt;




&lt;p&gt;Felicitaciones!!! 🎉🎉&lt;/p&gt;

&lt;p&gt;Ya tenemos nuestro primer &lt;strong&gt;API&lt;/strong&gt; desarrollado. En las próximas entregas iremos agregando poder a nuestro &lt;strong&gt;API&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No se olviden de bajar el código de &lt;a href="https://github.com/andreslozadamosto/MyFirstNetCoreAPI-Tutorial" rel="noopener noreferrer"&gt;Github&lt;/a&gt; y de dejar en los comentarios si quieren que veamos algo en particular en los próximos artículos de la serie.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>api</category>
      <category>dotnet</category>
      <category>rest</category>
    </item>
    <item>
      <title>Creando un API en Net Core - REST API</title>
      <dc:creator>Andres Lozada Mosto</dc:creator>
      <pubDate>Tue, 02 Feb 2021 05:01:18 +0000</pubDate>
      <link>https://dev.to/andreslozadamosto/creando-un-api-en-net-core-5-rest-api-3e2l</link>
      <guid>https://dev.to/andreslozadamosto/creando-un-api-en-net-core-5-rest-api-3e2l</guid>
      <description>&lt;p&gt;En el &lt;a href="https://dev.to/andreslozadamosto/creando-un-api-en-net-core-5-3jc4-temp-slug-8247256/edit"&gt;primer artículo&lt;/a&gt; de la serie vimos que es un &lt;strong&gt;API&lt;/strong&gt; y cuales son los tipos que hay. &lt;/p&gt;

&lt;p&gt;En el presente, vamos a enfocarnos sobre que es un &lt;strong&gt;API REST&lt;/strong&gt; y como podemos diseñarlas.&lt;/p&gt;

&lt;h1&gt;
  
  
  API REST
&lt;/h1&gt;

&lt;p&gt;Durante la última década (y mucho más también) las &lt;strong&gt;API REST&lt;/strong&gt; fueron el estándar de facto para desarrollar &lt;strong&gt;APIs&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;REST&lt;/strong&gt; &lt;em&gt;(REpresentational State Transfer)&lt;/em&gt; es un estilo de arquitectura de sistemas distribuidos y no un estándar como lo es &lt;strong&gt;SOAP&lt;/strong&gt;. Esta es la razón por la que no existe una guía estandarizada para su implementación; lo que si hay son principios o limitaciones que definen si un &lt;strong&gt;API&lt;/strong&gt; es &lt;strong&gt;REST&lt;/strong&gt; o no. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://roy.gbiv.com/"&gt;Roy Fielding&lt;/a&gt; fue la persona que introdujo este concepto por primera vez en su tesis &lt;a href="https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm"&gt;“Architectural Styles and the Design of Network-based Software Architectures”&lt;/a&gt; donde define las siguientes restricciones que se deben cumplir para que una arquitectura sea considerada &lt;strong&gt;REST&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Arquitectura cliente-servidor&lt;/strong&gt; &lt;em&gt;(client-server architecture)&lt;/em&gt;: dos aplicaciones actuando una como cliente la cual solicita recursos a un servidor el cual devuelve una respuesta ante la solicitud del cliente.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sin estado&lt;/strong&gt; &lt;em&gt;(stateless)&lt;/em&gt;: El servidor no guarda un estado entre diferentes solicitudes (request) de un cliente, o sea, cada solicitud es independiente del resto y debe auto-contener la información suficiente para regenerar el estado en cada request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache&lt;/strong&gt;: debe ser posible almacenar en un caché la respuesta del servidor y poder devolverla en las subsiguientes peticiones (que requieran el mismo recurso) sin necesidad de que llegue el request al servidor nuevamente.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interfaz uniforme&lt;/strong&gt; &lt;em&gt;(uniform interface)&lt;/em&gt;:  Incluye cuatro aspectos, identificación de recursos, gestión de recursos a través de representaciones, comunicaciones auto-descriptivas e hipermedia como el motor del estado de la aplicación (&lt;a href="https://es.wikipedia.org/wiki/HATEOAS"&gt;HATEOAS&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sistema de capas&lt;/strong&gt; &lt;em&gt;(layered system)&lt;/em&gt;: La solicitud al server puede pasar por diferentes capas donde se puede agregar funcionalidades adicionales como caché, seguridad, balanceo de carga, auditoría, logging, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Código bajo demanda&lt;/strong&gt; &lt;em&gt;(code on demand)&lt;/em&gt;: Es un feature opcional y no es necesario que las APIs lo implementen. Esta funcionalidad permite que el server devuelva al cliente código ejecutable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Las &lt;strong&gt;APIs&lt;/strong&gt; que implementan &lt;strong&gt;REST&lt;/strong&gt; se las conoce como &lt;strong&gt;API RESTful&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;En el último tiempo surgieron especificaciones que intentan darle un marco estandarizado a las &lt;strong&gt;API REST&lt;/strong&gt;. Entre ellas &lt;a href="https://www.openapis.org/"&gt;OpenAPI&lt;/a&gt; es la más popular. Otra especificación en auge es el &lt;a href="https://raml.org/"&gt;RAML&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O5zdMyDx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2yf9ya3hrvi7cmbbfhpt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O5zdMyDx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2yf9ya3hrvi7cmbbfhpt.jpg" alt="Its not restful" width="880" height="1245"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Diseño de un API REST
&lt;/h1&gt;

&lt;p&gt;Cuando diseñamos un &lt;strong&gt;API REST&lt;/strong&gt;, más allá de la implementación propia del lenguaje de programación que utilicemos, debemos tener en cuenta lo siguiente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Los principios analizados anteriormente&lt;/li&gt;
&lt;li&gt;Las &lt;strong&gt;API REST&lt;/strong&gt; trabajan con &lt;em&gt;"recursos"&lt;/em&gt; y no con &lt;em&gt;"servicios/funciones"&lt;/em&gt; como en &lt;strong&gt;RPC&lt;/strong&gt;/&lt;strong&gt;SOAP&lt;/strong&gt; y que cada recurso debe poder accederse por un URI en propio (endpoint)&lt;/li&gt;
&lt;li&gt;Debemos hacer uso correcto de las bondades del protocolo &lt;em&gt;HTTP&lt;/em&gt;, tales como los verbos (&lt;em&gt;GET&lt;/em&gt;, &lt;em&gt;POST&lt;/em&gt;, &lt;em&gt;PUT&lt;/em&gt;, etc), los códigos de estado (&lt;em&gt;404&lt;/em&gt;, &lt;em&gt;500&lt;/em&gt;, &lt;em&gt;200&lt;/em&gt;, etc) y las cabeceras (&lt;em&gt;headers&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Por ejemplo, tomemos el &lt;em&gt;recurso&lt;/em&gt; Usuario llamado &lt;em&gt;Andres&lt;/em&gt;, debe tener asociada una URI &lt;em&gt;&lt;a href="http://domain.com/users/andres"&gt;http://domain.com/users/andres&lt;/a&gt;&lt;/em&gt; y para su obtención se utiliza el método &lt;em&gt;GET&lt;/em&gt;, para su creación &lt;em&gt;POST&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Otro ejemplo es la información de una reunión &lt;em&gt;&lt;a href="http://domain.com/meetings/2354"&gt;http://domain.com/meetings/2354&lt;/a&gt;&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Tanto el recurso &lt;em&gt;“andres”&lt;/em&gt; como el recurso &lt;em&gt;“meeting 2354”&lt;/em&gt; siempre refieren al mismo recurso y es esperable que ante subsecuentes solicitudes siempre devuelva la misma información (la cual se puede cachear).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_ku_J-h2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/jruswtuc1l60hbntvwig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_ku_J-h2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/jruswtuc1l60hbntvwig.png" alt="Alt Text" width="880" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ejemplos no validos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="http://domain.com/users/getUserByName/andres"&gt;http://domain.com/users/getUserByName/andres&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="http://domain.com/users/updateUser/andres"&gt;http://domain.com/users/updateUser/andres&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="http://domain.com/users/createUserUser/andres"&gt;http://domain.com/users/createUserUser/andres&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Verbos HTTP (HTTP verbs)
&lt;/h2&gt;

&lt;p&gt;El &lt;a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html"&gt;protocolo HTTP&lt;/a&gt; define un conjunto de acciones a realizar sobre un recurso tales como su creación, su borrado o actualización. &lt;br&gt;
Estas acciones (HTTP verbs) juegan un rol importante en el constraint de Uniform Interface que vimos anteriormente.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Verbo HTTP&lt;/th&gt;
&lt;th&gt;Operación&lt;/th&gt;
&lt;th&gt;Descripción&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;td&gt;Solicita uno o una colección de recursos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;Create&lt;/td&gt;
&lt;td&gt;Crea un recurso con la información enviada en el cuerpo (body) del request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;Update/Replace&lt;/td&gt;
&lt;td&gt;Actualiza un recurso preexistente o lo reemplaza. Podría también crearlo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PATCH&lt;/td&gt;
&lt;td&gt;Update/Modify&lt;/td&gt;
&lt;td&gt;Similar a PUT, se utiliza para actualizar de manera parcial un recurso&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;td&gt;Delete&lt;/td&gt;
&lt;td&gt;Elimina un recurso&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HEAD&lt;/td&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;td&gt;Similar a GET, pero el server solo retorna los encabezados (headers) y no el cuerpo (body) de la respuesta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OPTIONS&lt;/td&gt;
&lt;td&gt;ASK&lt;/td&gt;
&lt;td&gt;El servidor retorna el listado de verbos HTTP permitidos para el recurso&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TRACE&lt;/td&gt;
&lt;td&gt;ALL&lt;/td&gt;
&lt;td&gt;Solicita al servidor que agregue en la respuesta los datos recibidos para depuración&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Los verbos &lt;em&gt;GET&lt;/em&gt;, &lt;em&gt;HEAD&lt;/em&gt;, &lt;em&gt;OPTIONS&lt;/em&gt; y &lt;em&gt;TRACE&lt;/em&gt; son considerados seguros por definición por lo que no deben generar un impacto en el/los recursos indicados en la petición (ni generar por ejemplo transacciones que generen un cambio de estado en el servidor) más allá de agregar logging, update de caché, entre otros recursos aledaños pero nunca sobre el recurso indicado en el URI.&lt;/p&gt;

&lt;p&gt;Por otro lado, muchos de estos &lt;em&gt;verbos HTTP&lt;/em&gt; tienden a ser idempotentes (aunque no es una restricción si es esperable). Los verbos &lt;em&gt;PUT&lt;/em&gt;, &lt;em&gt;DELETE&lt;/em&gt;, &lt;em&gt;GET&lt;/em&gt;, &lt;em&gt;HEAD&lt;/em&gt;, &lt;em&gt;OPTIONS&lt;/em&gt; y &lt;em&gt;TRACE&lt;/em&gt; deben devolver siempre la misma información ante subsecuentes requests. No así para el verbo &lt;em&gt;POST&lt;/em&gt; donde cada request igual puede generar efectos secundarios tal vez no deseados.&lt;/p&gt;

&lt;h2&gt;
  
  
  Código de estados (HTTP Status codes)
&lt;/h2&gt;

&lt;p&gt;Los datos que retorna el servidor a partir de un request siempre son acompañados por un código de estado de &lt;strong&gt;HTTP&lt;/strong&gt; que indica el resultado del request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTTP&lt;/strong&gt; define un conjunto predefinidos que se agrupan en:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1xx&lt;/strong&gt;: los códigos dentro de este rango son informativos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2xx&lt;/strong&gt;: los encontrados en este rango indican que la solicitud fue exitosa&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3xx&lt;/strong&gt;: este grupo de códigos indican que se debe hacer o hubo una redirección&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4xx&lt;/strong&gt;: los códigos dentro de este grupo indican diferentes errores del cliente&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5xx&lt;/strong&gt;: estos códigos indican que se produjo un error en el servidor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Muchos de estos códigos son devueltos por la infraestructura o por el framework que utilicemos para crear nuestra API, mientras que nosotros como devs vamos a utilizar otros (y en algunos casos los mismos) para indicar que sucedió con la solicitud que estamos tratando.&lt;/p&gt;

&lt;p&gt;Los códigos más utilizados son:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;200&lt;/strong&gt; &lt;em&gt;(OK)&lt;/em&gt;: solicitud exitosa y retorno del recurso&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;201&lt;/strong&gt; &lt;em&gt;(Created)&lt;/em&gt;: recurso creado exitosamente y retorna el URI al recurso&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;401&lt;/strong&gt; &lt;em&gt;(Unauthorized)&lt;/em&gt;: sin autorización para el recurso&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;403&lt;/strong&gt; &lt;em&gt;(Forbidden)&lt;/em&gt;: &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;404&lt;/strong&gt; &lt;em&gt;(Not Found)&lt;/em&gt;: recurso no encontrado/inexistente&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;409&lt;/strong&gt; &lt;em&gt;(Conflict)&lt;/em&gt;: conflicto en la creación/update del recurso&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;500&lt;/strong&gt; &lt;em&gt;(Internal Server Error)&lt;/em&gt;: error no manejado en el código de la aplicación, por ejemplo una excepción sin un catch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xcRrwwIV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/okqq15ra2sex0mohbchq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xcRrwwIV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/okqq15ra2sex0mohbchq.jpg" alt="Alt Text" width="700" height="809"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No comments sobre esta imagen 🤣&lt;/p&gt;

&lt;h2&gt;
  
  
  Encabezados (HTTP Headers)
&lt;/h2&gt;

&lt;p&gt;Los &lt;strong&gt;Headers&lt;/strong&gt; es información adicional que se intercambia entre el cliente y el servidor (Request/Response). Por ejemplo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Información sobre los datos enviados en el Request/Response&lt;/li&gt;
&lt;li&gt;Información de Autenticación&lt;/li&gt;
&lt;li&gt;Información sobre Caching &lt;/li&gt;
&lt;li&gt;Información sobre Cookies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Algunos &lt;strong&gt;Headers&lt;/strong&gt; tienen dirección cliente al servidor (ej: &lt;em&gt;Authentication&lt;/em&gt;), otros tienen significado del servidor al cliente (ej: &lt;em&gt;Location) y por último algunos se utilizan indistintamente (ej: _Content-Length&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Entre los mas comunes podemos mencionar&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Header&lt;/th&gt;
&lt;th&gt;Descripción&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Authorization&lt;/td&gt;
&lt;td&gt;Método e información para la autenticación en el servidor del recurso&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache-Control&lt;/td&gt;
&lt;td&gt;Método de cache para el recurso&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accept&lt;/td&gt;
&lt;td&gt;Informa sobre el formato aceptado por el cliente&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accept-Language&lt;/td&gt;
&lt;td&gt;Lenguaje esperado por el cliente para los datos que recibe del server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cookie&lt;/td&gt;
&lt;td&gt;Cookies recibidas por el server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Origin&lt;/td&gt;
&lt;td&gt;Indica desde donde se origino el request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Content-Length&lt;/td&gt;
&lt;td&gt;Tamaño del payload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Content-Type&lt;/td&gt;
&lt;td&gt;Indica el Media Type del payload&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;Bien ya aprendimos algo sobre las &lt;strong&gt;API REST&lt;/strong&gt; es hora de comenzar a meter mano un algo de código, somos devs, no?&lt;/p&gt;

&lt;p&gt;Bueno, preparate un 🧉/☕ + 🥐🍩 que en el próximo vamos directo a  tirar magia 💪 &lt;/p&gt;




</description>
      <category>csharp</category>
      <category>api</category>
      <category>dotnet</category>
      <category>rest</category>
    </item>
    <item>
      <title>Creando un API en Net Core - Intro</title>
      <dc:creator>Andres Lozada Mosto</dc:creator>
      <pubDate>Tue, 02 Feb 2021 04:59:53 +0000</pubDate>
      <link>https://dev.to/andreslozadamosto/creando-un-api-en-net-core-5-intro-2nc2</link>
      <guid>https://dev.to/andreslozadamosto/creando-un-api-en-net-core-5-intro-2nc2</guid>
      <description>&lt;p&gt;Hace tiempo que no publicaba nada y me pareció una buena idea repasar a que estamos llamando &lt;strong&gt;API&lt;/strong&gt; y más precisamente a un &lt;strong&gt;API REST&lt;/strong&gt;. Cuáles son las consideraciones que debemos tener en cuenta al momento de crearla y finalmente cuáles son las herramientas que &lt;strong&gt;&lt;em&gt;Net Core&lt;/em&gt;&lt;/strong&gt; nos provee para desarrollarla.&lt;/p&gt;

&lt;p&gt;El objetivo principal de este grupo de artículos es para los desarrolladores que recién están dando sus primeros pasos puedan comprender de una manera simple y directa que es un API y cómo hacer una implementación completa utilizando &lt;strong&gt;&lt;em&gt;Net Core&lt;/em&gt;&lt;/strong&gt;. Desarrolladores más experimentados pueden tomar esto como una guía/checklist o mejor aún ayudarme con posibles olvidos, correcciones o lo que deseen aportar para ayudar a los que recién comienzan 💪🤙&lt;/p&gt;

&lt;h1&gt;
  
  
  ¿Qué es un API?
&lt;/h1&gt;

&lt;p&gt;Antes de comenzar a tirar código como bien techies que somos sentemos una base sobre lo que entendemos que es un &lt;strong&gt;API&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;API&lt;/strong&gt;: Interfaz de programación de aplicaciones o bien en inglés Application programming interface.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ya solo con el nombre nos está dando una pista, es un interfaz para interactuar con componentes de un software sin necesidad de conocer su implementación concreta. Entonces, es una abstracción para el desarrollador que utiliza el &lt;strong&gt;API&lt;/strong&gt;. Toda &lt;strong&gt;API&lt;/strong&gt; provee un contrato donde estipula la forma en que se la puede utilizar.&lt;/p&gt;




&lt;h1&gt;
  
  
  ¿Dónde podemos ver ejemplos de APIs?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a5ntUdLb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/da922k2ju75m8r5of5xf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a5ntUdLb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/da922k2ju75m8r5of5xf.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hay muchos ejemplos de &lt;strong&gt;APIs&lt;/strong&gt;, veamos algunos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Net Core&lt;/em&gt;&lt;/strong&gt; y &lt;strong&gt;&lt;em&gt;C#&lt;/em&gt;&lt;/strong&gt; proveen un API con el cual desarrollamos las aplicaciones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Google Maps&lt;/em&gt;&lt;/strong&gt; nos provee un &lt;strong&gt;API&lt;/strong&gt; con el cual podemos insertar su mapa en nuestra aplicación&lt;/li&gt;
&lt;li&gt;Hay &lt;strong&gt;APIs&lt;/strong&gt; web públicas para por ejemplo obtener el clima de una ciudad o realizar conversiones de moneda e integrarlas en nuestras aplicaciones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Amazon&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;eBay&lt;/em&gt;&lt;/strong&gt; o &lt;strong&gt;&lt;em&gt;Mercado Libre&lt;/em&gt;&lt;/strong&gt; proveen un &lt;strong&gt;API&lt;/strong&gt; público de búsqueda para su catálogo&lt;/li&gt;
&lt;li&gt;Los &lt;strong&gt;&lt;em&gt;sistemas operativos&lt;/em&gt;&lt;/strong&gt; proveen &lt;strong&gt;APIs para&lt;/strong&gt; que sus componentes internos puedan interactuar&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Kubernetes&lt;/em&gt;&lt;/strong&gt; provee un &lt;strong&gt;API&lt;/strong&gt; para su administración&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Como se puede ver, hay muchos tipos de &lt;strong&gt;APIs&lt;/strong&gt;. Algunas son &lt;em&gt;internas&lt;/em&gt; (como por ejemplo las del &lt;strong&gt;&lt;em&gt;SOs&lt;/em&gt;&lt;/strong&gt; o &lt;strong&gt;&lt;em&gt;Net Core&lt;/em&gt;&lt;/strong&gt;) y otras son para &lt;em&gt;consumirlas remotamente&lt;/em&gt; (por ejemplo el API de clima). Son estas últimas las que nos vamos a estar enfocando en estos artículos y son las que comúnmente los desarrolladores les llamamos &lt;strong&gt;API&lt;/strong&gt; en el sentido amplio de la palabra.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Pregunta para conversar en los comentarios&lt;/em&gt;: El tablero del auto ¿es el API del vehículo hacia el conductor? 🤔 &lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  APIs Remotas
&lt;/h1&gt;

&lt;p&gt;Hablamos que lo que nos interesa son las &lt;strong&gt;APIs remotas&lt;/strong&gt;, excelente. Una &lt;strong&gt;API remota&lt;/strong&gt; requiere que 2 puntos no conectados de manera directa necesiten comunicarse y el medio utilizado es la red (Internet). El caso más visible es el de &lt;strong&gt;&lt;em&gt;Gmail&lt;/em&gt;&lt;/strong&gt; donde el Frontend que se ejecuta en el browser local de nuestra PC llama a una &lt;strong&gt;API remota&lt;/strong&gt; localizada en los servidores en &lt;strong&gt;&lt;em&gt;Google&lt;/em&gt;&lt;/strong&gt; para buscar los emails. Se puede extender a comunicación entre sistemas y aplicaciones de cualquier índole diferentes que pertenezcan o no a una misma empresa.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rLhoj2Eg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/69yh9pleli7kyk8gkvm4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rLhoj2Eg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/69yh9pleli7kyk8gkvm4.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lo más común o la norma general es que la comunicación que mencionamos en el párrafo anterior (o llamadas a las &lt;strong&gt;APIs&lt;/strong&gt;) se realice por medio del protocolo &lt;em&gt;HTTP/HTTPs&lt;/em&gt; y podemos dividirlos de la siguiente manera:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;RPC&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;(Remote Procedure Call)&lt;/em&gt;, es la ejecución de una función de un proceso en una máquina de la red interna o de internet. Por lo general su forma de comunicación es binaria y existe desde antes del auge de Internet. Como variantes aparecieron &lt;em&gt;XML-RPC&lt;/em&gt; y &lt;em&gt;JSON-RPC&lt;/em&gt;. En los últimos años tomó mucho auge &lt;em&gt;GRPC&lt;/em&gt; que convierte &lt;em&gt;RPC&lt;/em&gt; a web nativo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;SOAP&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;(Simple Object Access Protocol)&lt;/em&gt;, donde la comunicación está predefinida por medio de XML estándar y es la base de las &lt;em&gt;arquitecturas SOA&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;REST&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;(REpresentational State Transfer)&lt;/em&gt;, donde la comunicación se basa en &lt;em&gt;HTTP&lt;/em&gt; y utiliza todos sus features para generar las &lt;strong&gt;&lt;em&gt;APIs&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Estas últimas, en específico, son las que nos vamos a enfocar. Cuando los devs hablan de &lt;strong&gt;APIs&lt;/strong&gt; generalizamos la palabra a este tipo de &lt;strong&gt;API&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Pregunta para conversar en los comentarios&lt;/em&gt;: Cuando usamos el control remoto de la TV y cambiamos de canal. ¿Estamos llamando a una API remota? 🤔 &lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Este artículo solo fue la introducción al mundo de las API. &lt;/p&gt;

&lt;p&gt;Te invito a dejar tu comentario sobre las preguntas que aparecieron a lo largo del artículo y vamos conversando entre todos 😀💪&lt;/p&gt;




</description>
      <category>csharp</category>
      <category>api</category>
      <category>dotnet</category>
      <category>rest</category>
    </item>
    <item>
      <title>Moq and how creating Mocks with Linq</title>
      <dc:creator>Andres Lozada Mosto</dc:creator>
      <pubDate>Thu, 22 Oct 2020 14:17:26 +0000</pubDate>
      <link>https://dev.to/andreslozadamosto/moq-and-how-creating-mocks-with-linq-2co7</link>
      <guid>https://dev.to/andreslozadamosto/moq-and-how-creating-mocks-with-linq-2co7</guid>
      <description>&lt;p&gt;There is a not so known feature in &lt;em&gt;&lt;strong&gt;Moq&lt;/strong&gt;&lt;/em&gt; library and is the possibility to create our Mocks with &lt;em&gt;Linq&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Benefit to use &lt;em&gt;Linq&lt;/em&gt; to create our mocks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove boilerplate code&lt;/li&gt;
&lt;li&gt;Removing &lt;code&gt;Setup()&lt;/code&gt;call for each member we want configurate&lt;/li&gt;
&lt;li&gt;Remove calling &lt;code&gt;.Object&lt;/code&gt; when we use the object mocked&lt;/li&gt;
&lt;li&gt;We still able to Verify our mocks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, let’s see an example 😎&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple &amp;amp; hierarchy/recursive properties
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Andres"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;street&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"14th Street NW"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userModel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Of&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Username&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Street&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;street&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Methods with any parameter matching
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userModel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Of&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userList&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Fixture&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;With&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Of&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;())&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ActiveUsers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;userList&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify methods
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userAdded&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Of&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;());&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()),&lt;/span&gt; &lt;span class="n"&gt;Times&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Once&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;What do you think? will you start using &lt;em&gt;Linq&lt;/em&gt; to write your &lt;em&gt;Mocks&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;Fell free to share your thoughts on the comments 😎 💪&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Moq vs NSubstitute - Who is the winner?</title>
      <dc:creator>Andres Lozada Mosto</dc:creator>
      <pubDate>Fri, 16 Oct 2020 16:31:32 +0000</pubDate>
      <link>https://dev.to/cloudx/moq-vs-nsubstitute-who-is-the-winner-40gi</link>
      <guid>https://dev.to/cloudx/moq-vs-nsubstitute-who-is-the-winner-40gi</guid>
      <description>&lt;p&gt;In this article will compare two of most important mocking libraries for &lt;em&gt;.Net&lt;/em&gt; (&lt;em&gt;.Net Core&lt;/em&gt; and &lt;em&gt;.Net Framework&lt;/em&gt;) &lt;em&gt;&lt;strong&gt;NSubstitute&lt;/strong&gt;&lt;/em&gt; and &lt;em&gt;&lt;strong&gt;Moq&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;What will we compare?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simplicity&lt;/li&gt;
&lt;li&gt;Readability&lt;/li&gt;
&lt;li&gt;Behaviours&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;The fight begins 💪&lt;/p&gt;

&lt;h1&gt;
  
  
  Mock creation
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Moq
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NSubstitute
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Substitute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;For&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;strong&gt;Moq&lt;/strong&gt;&lt;/em&gt; uses a more representative notation. Anyone, just reading that line can know that we are creating a mock object.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Winner: &lt;em&gt;&lt;strong&gt;Moq&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Mocking properties
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Moq
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simple properties&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Hierarchy&lt;/span&gt;
&lt;span class="n"&gt;mockUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Street&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;street&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NSubstitute
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simple properties&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Hierarchy&lt;/span&gt;
&lt;span class="n"&gt;mockUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Street&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;street&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;strong&gt;NSubstitue&lt;/strong&gt;&lt;/em&gt; has a simpler interface to set up the returns.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Winner: &lt;em&gt;&lt;strong&gt;NSubstitue&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Mocking methods
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Moq
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Without parameters&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ActiveUsers&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Matching by value&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SearchById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Matching by any value of the parameter type&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SearchById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;())).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Matching by custom logic&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SearchById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NSubstitute
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Without parameters&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ActiveUsers&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Matching by value&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SearchById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Matching by any value of the parameter type&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SearchById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Arg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Matching by custom logic&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SearchById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Arg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same here, &lt;em&gt;&lt;strong&gt;NSubstitute&lt;/strong&gt;&lt;/em&gt; has a simpler interface to set up the returns of the methods mocked.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Winner: &lt;em&gt;&lt;strong&gt;NSubstitue&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Matching arguments
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Moq
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// - It.IsAny&amp;lt;int&amp;gt;())&lt;/span&gt;
&lt;span class="c1"&gt;// - It.IsInRange(0, 10, Range.Inclusive) &lt;/span&gt;
&lt;span class="c1"&gt;// - It.IsIn(Enumerable.Range(1, 5))&lt;/span&gt;
&lt;span class="c1"&gt;// - It.IsNotIn(Enumerable.Range(1, 5))&lt;/span&gt;
&lt;span class="c1"&gt;// - It.IsNotNull&amp;lt;string&amp;gt;())&lt;/span&gt;
&lt;span class="c1"&gt;// - It.IsRegex("abc"))&lt;/span&gt;
&lt;span class="c1"&gt;// - It.Is&amp;lt;int&amp;gt;(i =&amp;gt; i &amp;lt; 10))&lt;/span&gt;
&lt;span class="c1"&gt;// - mock.Setup(x =&amp;gt; x.Search(IsLarge())) //&amp;lt; custom&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NSubstitute
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// - Arg.Any&amp;lt;int&amp;gt;())&lt;/span&gt;
&lt;span class="c1"&gt;// - Arg.Is&amp;lt;int&amp;gt;(i =&amp;gt; i &amp;lt; 10))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;strong&gt;Moq&lt;/strong&gt;&lt;/em&gt; provide us a lot more built-in options for matching arguments&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Winner: &lt;em&gt;&lt;strong&gt;Moq&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Capturing parameter
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Moq
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;())).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;IUserModel&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Username&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"Andres"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NSubstitute
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Arg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;IUserModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="n"&gt;Username&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"Andres"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;em&gt;&lt;strong&gt;Moq&lt;/strong&gt;&lt;/em&gt;, it is easier to get the arguments values before returning the mocked value and process them as needed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Winner: &lt;em&gt;&lt;strong&gt;Moq&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Multiple matching
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Moq
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SearchById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;())).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;userList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SearchById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;userList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NSubstitute
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SearchById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Arg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SearchById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both of them allow us to add as many match expressions that we need. If there is a conflict they will take the last expression that matches.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Winner: &lt;em&gt;&lt;strong&gt;Tie&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Callabacks
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Moq
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SearchById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Callback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;parameterValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Methods without return&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;Callback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"aa"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// before and after&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SearchById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Callback&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;parameterValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Callback&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"andres"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NSubstitute
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SearchById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReturnsForAnyArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;parameterValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;userList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Methods without return&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;When&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"aa"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;span class="c1"&gt;// before and after&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SearchById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
               &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReturnsForAnyArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
               &lt;span class="p"&gt;{&lt;/span&gt;
                   &lt;span class="n"&gt;parameterValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;userList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
               &lt;span class="p"&gt;})&lt;/span&gt;
               &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AndDoes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"andres"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case &lt;em&gt;&lt;strong&gt;Moq&lt;/strong&gt;*&lt;/em&gt; provides a more readable experience adding a function called “Callback” on the chain call.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Winner: &lt;em&gt;&lt;strong&gt;Moq&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Multi-returns
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Moq
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetupSequence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;users1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;users2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;users3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NSubstitute
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Arg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;users1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;users2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;users3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here both libraries are very similar right?.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Winner: &lt;em&gt;&lt;strong&gt;Tie&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Throwing Exceptions
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Moq
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="n"&gt;Throws&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;Throws&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"msj"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NSubstitute
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ActiveUsers&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;When&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"msj"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;strong&gt;Moq&lt;/strong&gt;&lt;/em&gt; is easier to recognize that the function will throw an exception.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Winner: &lt;em&gt;&lt;strong&gt;Moq&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Verify
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Moq
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Setter&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;VerifySet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Getter&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;VerifyGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Methods with matchting&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()));&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"aaa"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;Times&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Never&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Occurrences&lt;/span&gt;
&lt;span class="c1"&gt;// - Times.Never&lt;/span&gt;
&lt;span class="c1"&gt;// - Times.Once&lt;/span&gt;
&lt;span class="c1"&gt;// - Times.AtLeastOnce&lt;/span&gt;
&lt;span class="c1"&gt;// - Times.AtMost(2)&lt;/span&gt;
&lt;span class="c1"&gt;// - Times.Exactly(2)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NSubstitute
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Setter&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Received&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Getter&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Received&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Methods with matchting&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Received&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"aa"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DidNotReceive&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"aaa"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Received&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bb"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Occurrences&lt;/span&gt;
&lt;span class="c1"&gt;// Nop&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;strong&gt;NSubsitute&lt;/strong&gt;&lt;/em&gt; has a simple way to verify that a method was called but Moq has more options to test how many times the method was executed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Winner: &lt;em&gt;&lt;strong&gt;Moq&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Matching Generic Type Arguments
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Moq
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSubtype&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;())).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserModel2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;())).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NSubstitute
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddUser&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;Arg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddUser&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;Arg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserModel2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, both provide an easy way to mock Generic Methods&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Winner: &lt;em&gt;&lt;strong&gt;tie&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Well, how is it going? Who is winning? &lt;br&gt;
Let’s see the current score&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Syntax&lt;/th&gt;
&lt;th&gt;Moq&lt;/th&gt;
&lt;th&gt;NSubstitute&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mock creation&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mocking properties&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mocking methods&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Matching arguments&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiple matching&lt;/td&gt;
&lt;td&gt;tie&lt;/td&gt;
&lt;td&gt;tie&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Callabacks&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-returns&lt;/td&gt;
&lt;td&gt;tie&lt;/td&gt;
&lt;td&gt;tie&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Throwing Exceptions&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verify&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Matching Generic Type Arguments&lt;/td&gt;
&lt;td&gt;tie&lt;/td&gt;
&lt;td&gt;tie&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Moq&lt;/strong&gt;&lt;/em&gt; is the winner!! 😀 🎉&lt;/p&gt;

&lt;p&gt;I really prefer &lt;em&gt;&lt;strong&gt;Moq&lt;/strong&gt;&lt;/em&gt; and I try to use it always but both are excellent options to use. Us, as developers, we should be able to work with both.&lt;/p&gt;

&lt;p&gt;You can download the examples from my Github&lt;br&gt;
&lt;a href="https://github.com/andreslozadamosto/net-thoughts/tree/master/libraries/testing/MoqVsNSubstitute/MockingLibrariesExamples"&gt;https://github.com/andreslozadamosto/net-thoughts/tree/master/libraries/testing/MoqVsNSubstitute/MockingLibrariesExamples&lt;/a&gt; &lt;/p&gt;




&lt;p&gt;Are you with me? &lt;br&gt;
Do you prefer &lt;em&gt;&lt;strong&gt;NSubstitue&lt;/strong&gt;&lt;/em&gt;? &lt;br&gt;
I forget to compare any behaviour?&lt;/p&gt;

&lt;p&gt;Share your comments 🤙👇&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Kafka + Docker + Net Core 101 - Part 1</title>
      <dc:creator>Andres Lozada Mosto</dc:creator>
      <pubDate>Mon, 21 Sep 2020 15:38:15 +0000</pubDate>
      <link>https://dev.to/cloudx/kafka-docker-net-core-101-part-1-b0h</link>
      <guid>https://dev.to/cloudx/kafka-docker-net-core-101-part-1-b0h</guid>
      <description>&lt;p&gt;If you are here, you know what we are talking about and you want to get your hands dirty as soon as possible, so this post is for you 😀.&lt;/p&gt;

&lt;p&gt;If you are not familiar with &lt;strong&gt;Kafka&lt;/strong&gt; or you are not sure about what it's used for, you can visit these links.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="https://medium.com/@sonusharma.mnnit/apache-kafka-in-depth-49aae1e844be" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F-dPKGsx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/fit/c/56/56/1%2A0DC89DfVpGHJ-Gj3AFCXvQ.jpeg" alt="Sonu Sharma"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://medium.com/@sonusharma.mnnit/apache-kafka-in-depth-49aae1e844be" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Apache Kafka in Depth. In the era of Big Data, lots and lots… | by Sonu Sharma | Medium&lt;/h2&gt;
      &lt;h3&gt;Sonu Sharma ・ &lt;time&gt;Jul 24, 2019&lt;/time&gt; ・ 9 min read
      &lt;div class="ltag__link__servicename"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KBvj_QRD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/medium_icon-90d5232a5da2369849f285fa499c8005e750a788fdbf34f5844d5f2201aae736.svg" alt="Medium Logo"&gt;
        Medium
      &lt;/div&gt;
    &lt;/h3&gt;
&lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;div class="ltag__link"&gt;
  &lt;a href="https://medium.com/@rinu.gour123/kafka-for-beginners-74ec101bc82d" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pW2khHsr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/fit/c/56/56/1%2AwUv5jHLVJnmUjE6vbQjF6Q.jpeg" alt="Rinu Gour"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://medium.com/@rinu.gour123/kafka-for-beginners-74ec101bc82d" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Kafka For Beginners. What is Kafka? | by Rinu Gour | Medium&lt;/h2&gt;
      &lt;h3&gt;Rinu Gour ・ &lt;time&gt;Sep 12, 2018&lt;/time&gt; ・ 9 min read
      &lt;div class="ltag__link__servicename"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KBvj_QRD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/medium_icon-90d5232a5da2369849f285fa499c8005e750a788fdbf34f5844d5f2201aae736.svg" alt="Medium Logo"&gt;
        Medium
      &lt;/div&gt;
    &lt;/h3&gt;
&lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Ok, we already know what Kafka can be used for, so let's try to run it locally, test basic commands, and create a basic app to publish events and another one to use them using &lt;strong&gt;Net Core&lt;/strong&gt;. &lt;/p&gt;

&lt;h1&gt;
  
  
  Kafka + Docker
&lt;/h1&gt;

&lt;p&gt;In order to set up our environment, we create a Docker Compose file where we will instantiate a Zookeeper service and a Kafka service (you can then set up additional ones and build the clusters).&lt;/p&gt;

&lt;p&gt;The base images we are going to use are the ones from our &lt;em&gt;&lt;a href="https://github.com/confluentinc/cp-docker-images"&gt;Confluence&lt;/a&gt;&lt;/em&gt; friends.&lt;br&gt;
This is not the only option, you can also use the images from &lt;em&gt;&lt;a href="https://bitnami.com/stack/zookeeper/containers"&gt;Bitmani|Zookeeper&lt;/a&gt;&lt;/em&gt;, &lt;em&gt;&lt;a href="https://bitnami.com/stack/kafka/containers"&gt;Bitmani|Kafka&lt;/a&gt;&lt;/em&gt;, &lt;em&gt;&lt;a href="https://hub.docker.com/r/spotify/kafka/"&gt;Spotify&lt;/a&gt;&lt;/em&gt; or &lt;em&gt;&lt;a href="https://github.com/wurstmeister/kafka-docker"&gt;Wurstmeister&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Our docker-compose file results as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '2'
services:
    zookeeper:
        image: confluentinc/cp-zookeeper:latest
        environment:
            ZOOKEEPER_CLIENT_PORT: 2181
            ZOOKEEPER_TICK_TIME: 2000
    kafka:
        image: confluentinc/cp-kafka:latest
        depends_on:
            - zookeeper
        ports:
            - 9092:9092
        environment:
            KAFKA_BROKER_ID: 1
            KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
            KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://127.0.0.1:9092
            KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
            KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
            KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; To access &lt;strong&gt;Kafka&lt;/strong&gt; from outside of the container, you must replace the &lt;code&gt;localhost&lt;/code&gt; at &lt;em&gt;ADVERTISED&lt;/em&gt; with the Docker-Machine IP.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Setting up the environment with Docker Tools (Windows)
&lt;/h2&gt;

&lt;p&gt;Once a docker compose file is created we can set our environment.&lt;/p&gt;

&lt;p&gt;Let's open the docker console and create a new &lt;em&gt;Docker Machine&lt;/em&gt; running this command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-machine create &lt;span class="nt"&gt;--driver&lt;/span&gt; virtualbox confluent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We are going to use the VirtualBox driver to create the host virtual machine and we will call it confluent (we can use any name, however)&lt;/p&gt;

&lt;p&gt;If we use Linux or Windows Professional onwards, we can skip this option and go straight to the &lt;code&gt;docker-compose up&lt;/code&gt;. However, for those who use Windows 10 Home or older versions with Docker Toolbox, this is a step that we must do to have it running the same way.&lt;/p&gt;

&lt;p&gt;Let's check that Docker Machine has been created.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-machine &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z0ZxSEv5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/p2o9x084hm4ka2lpcgc0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z0ZxSEv5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/p2o9x084hm4ka2lpcgc0.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we have to go to the &lt;em&gt;docker machine&lt;/em&gt; that we have just created so that the commands can be executed on it (otherwise it will be done on the default)&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;docker-machine &lt;span class="nb"&gt;env &lt;/span&gt;confluent&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We get the IP so we will be able to connect from &lt;em&gt;C#&lt;/em&gt; and use it through the environment variable &lt;em&gt;KAFKA_ADVERTISED_LISTENERS&lt;/em&gt; from the docker compose file.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-machine ip confluent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Finally, we run &lt;code&gt;docker-compose&lt;/code&gt; in the folder where the file yaml was saved.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We check that Kafka and Zookeeper are up and running.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Setting up the environment on Linux or Docker for Windows
&lt;/h2&gt;

&lt;p&gt;We simply run Docker Compose&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you are using WSL, you can get the IP in this way:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsl ip -a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With this tiny configuration, we get Kafka ready to the first testings, let's go!&lt;/p&gt;
&lt;h1&gt;
  
  
  Let's have fun with Kafka
&lt;/h1&gt;

&lt;p&gt;By default, &lt;strong&gt;Kafka&lt;/strong&gt; comes with scripts to create topics, publish/read messages, etc. During this part we will see how to work with them.&lt;/p&gt;
&lt;h3&gt;
  
  
  Create a new topic
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container &lt;span class="nb"&gt;exec&lt;/span&gt; &amp;lt;kafka-container-id&amp;gt; kafka-topics &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--create&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--topic&lt;/span&gt; foo &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--partitions&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--replication-factor&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--if-not-exists&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--zookeeper&lt;/span&gt; zookeeper:2181
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We create a new &lt;em&gt;Topic&lt;/em&gt; in &lt;strong&gt;Kafka&lt;/strong&gt; with a replication factor of 1 and with only one partition. If we wanted to increase these values we should build up other Kafka instances.&lt;br&gt;
The parameter &lt;em&gt;--if-not-exists&lt;/em&gt; indicates that the topic will be created if it doesn't previously exist.&lt;/p&gt;
&lt;h2&gt;
  
  
  List the topics
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container &lt;span class="nb"&gt;exec&lt;/span&gt; &amp;lt;kafka-container-id&amp;gt; kafka-topics &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--list&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--zookeeper&lt;/span&gt; zookeeper:2181
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This command will list only the names of the &lt;em&gt;Topics&lt;/em&gt; we have already created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CcZXMPza--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wd0pyi8bl6v2p6s898nf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CcZXMPza--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wd0pyi8bl6v2p6s898nf.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  See a topic’s description
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container &lt;span class="nb"&gt;exec&lt;/span&gt; &amp;lt;kafka-container-id&amp;gt; kafka-topics &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--describe&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--topic&lt;/span&gt; foo &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--zookeeper&lt;/span&gt; zookeeper:2181
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It shows relevant information about the topic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ApUajDGN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6ejhnpgm0dfkci9a8qy5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ApUajDGN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6ejhnpgm0dfkci9a8qy5.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Publish a message to the topic
&lt;/h2&gt;

&lt;p&gt;In this case it is better to open your Kafka’s container console, and execute the Producer from there.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;kafka-container-id&amp;gt; /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now we execute the &lt;em&gt;Publisher&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kafka-console-producer &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--request-required-acks&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--broker-list&lt;/span&gt; &amp;lt;docker-machine-ip&amp;gt;:9092 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--topic&lt;/span&gt; foo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then, for each line we write (separated by a line break), a message will be sent (we use &lt;em&gt;[CTRL+C]&lt;/em&gt; to exit the command and Exit to exit the container).&lt;/p&gt;
&lt;h3&gt;
  
  
  Read from a topic
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container &lt;span class="nb"&gt;exec&lt;/span&gt; &amp;lt;kafka-container-id&amp;gt; kafka-console-consumer &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--bootstrap-server&lt;/span&gt; &amp;lt;docker-machine-ip&amp;gt;:9092 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--topic&lt;/span&gt; foo &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--zookeeper&lt;/span&gt;  zookeeper:2181
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;These are the basics to get started and run the first tests. Each command has multiple parameters worth investigating in order to make the most of it.&lt;/p&gt;

&lt;p&gt;For further details, you can check the &lt;a href="https://kafka.apache.org/documentation/#operations"&gt;official docs&lt;/a&gt;.&lt;/p&gt;


&lt;h1&gt;
  
  
  C# knows Kafka
&lt;/h1&gt;

&lt;p&gt;We already have &lt;strong&gt;Kafka&lt;/strong&gt; executing on our PC, now let’s take a look at how to publish and read messages from a &lt;em&gt;Topic&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Producer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We create a console application, and we use the following code to replace the code you will find in Docker-Machine.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;We compile and execute, indicating the name of the topic we want to send messages to. The application keeps waiting for our input. Each message should be written in a different line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet build
&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
dotnet run foo

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Consumer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the Consumer, we create a new console application and we replace the IP value for the IP value of the Docker-Machine we are using.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;We compile and run, indicating which &lt;em&gt;Topic&lt;/em&gt; we want to listen to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet build
&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
dotnet run foo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While executing both programs and displaying one console next to the other, we can see that, whenever we publish a message, the listening console displays it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5htkNK9C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/n8z96etnwrzm3naeu08n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5htkNK9C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/n8z96etnwrzm3naeu08n.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can download the source code from &lt;a href="https://github.com/andreslozada/net-thoughts/tree/master/kafka/examples/parte1"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Kafka tools
&lt;/h1&gt;

&lt;p&gt;The universe of Kafka tools is immense, and in these lists you can find lots of resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/monksy/awesome-kafka"&gt;https://github.com/monksy/awesome-kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/semantalytics/awesome-kafka"&gt;https://github.com/semantalytics/awesome-kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dharmeshkakadia/awesome-kafka#testing"&gt;https://github.com/dharmeshkakadia/awesome-kafka#testing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are several tools for using Kafka via UI instead of the command line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.conduktor.io/"&gt;Conduktor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.kafkatool.com/"&gt;KafkaTool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/obsidiandynamics/kafdrop"&gt;Kafdrop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are other choices out there…&lt;/p&gt;

&lt;p&gt;Among the ones I tried, &lt;em&gt;Conduktor&lt;/em&gt; seems complete enough, despite the free version having some limitations. The good thing is that it includes integration to connect to &lt;em&gt;Confluent&lt;/em&gt; and &lt;em&gt;Aiven&lt;/em&gt;, in order to use their &lt;em&gt;Kafka SaaS platforms&lt;/em&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Closing up
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Kafka&lt;/strong&gt; is a monster that provides us with a whole new universe of possibilities to extend our architecture in unthinkable ways, but we should not be afraid to get started.&lt;/p&gt;

&lt;p&gt;We have seen that with little work (thanks to the people who created Docker images) we can setup everything we need to have Kafka up and running on Docker on our PC, and how easy it is to communicate with it from C#.&lt;/p&gt;

&lt;p&gt;In the future releases of this series of articles we will take a look to how can we continue digging further into it.&lt;/p&gt;

&lt;p&gt;🧉 + 🥐🥐 = Happy coder 💻&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Pursuit of happiness - oh my Windows terminal</title>
      <dc:creator>Andres Lozada Mosto</dc:creator>
      <pubDate>Mon, 21 Sep 2020 15:03:21 +0000</pubDate>
      <link>https://dev.to/cloudx/pursuit-of-happiness-oh-my-windows-terminal-18m5</link>
      <guid>https://dev.to/cloudx/pursuit-of-happiness-oh-my-windows-terminal-18m5</guid>
      <description>&lt;p&gt;What do you think if I tell you that your &lt;em&gt;PowerShell&lt;/em&gt; can look like this? It's pretty right? 😍 🤩&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgrucyh6llc1k10amckwr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgrucyh6llc1k10amckwr.jpg" alt="Windows Terminal + PowerShell Core looks great"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After reading this post, you'll get this beautiful console that you'll love to work with. And not only on &lt;em&gt;PowerShell&lt;/em&gt;, but on &lt;em&gt;WSL&lt;/em&gt; and &lt;em&gt;Azure Cloud Shell&lt;/em&gt; too!!  🥳 🤟&lt;/p&gt;

&lt;h1&gt;
  
  
  Terminal alternatives
&lt;/h1&gt;

&lt;p&gt;There are more options than the Windows Command Line (CMD) or Windows PowerShell. While they are mostly emulators that run on the terminal, they add good features and look better than the default consoles.&lt;/p&gt;

&lt;p&gt;Alternatives over there:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cmder.net/" rel="noopener noreferrer"&gt;CMDer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://conemu.github.io/" rel="noopener noreferrer"&gt;ConEmu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/microsoft/terminal" rel="noopener noreferrer"&gt;Windows Terminal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hyper.is/" rel="noopener noreferrer"&gt;Hyper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/alacritty/alacritty" rel="noopener noreferrer"&gt;Alacritty&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/@bhupathy/install-terminator-on-windows-with-wsl-2826591d2156" rel="noopener noreferrer"&gt;Terminator&lt;/a&gt; (running over WSL)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/felixse/FluentTerminal" rel="noopener noreferrer"&gt;Fluent Terminal&lt;/a&gt; I just found it and looks great&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I like to keep my dev environment simple so my choice is the new Windows Terminal using &lt;em&gt;PowerShell Core&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Why &lt;em&gt;Windows Terminal&lt;/em&gt;? Microsoft listened to the community and created a totally brand new terminal which is really easy to use, have a lot of cool features like tabs, panes and it can run &lt;em&gt;PowerShell&lt;/em&gt;, &lt;em&gt;CMD&lt;/em&gt;, &lt;em&gt;Git bash&lt;/em&gt;, &lt;em&gt;Linux bash&lt;/em&gt;, &lt;em&gt;Azure Cloud Shell&lt;/em&gt; as well as any other. &lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Windows terminal&lt;/em&gt; is relatively new. Previously I worked for many years with &lt;em&gt;ConEmu&lt;/em&gt; and I can say that it is a very good option too&lt;/p&gt;

&lt;p&gt;I encourage you to test all these alternatives and find which one fit best with you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz5b2haktir836gfds5sg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz5b2haktir836gfds5sg.gif" alt="Choose your best fit"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article is based on &lt;em&gt;Windows Terminal&lt;/em&gt;, but the configuration can be applied to the other tools as well.&lt;/p&gt;

&lt;h1&gt;
  
  
  Installation
&lt;/h1&gt;

&lt;p&gt;Let's see what we need to install to convert our boring terminal to an awesome one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/dahlbyk/posh-git" rel="noopener noreferrer"&gt;Posh-Git&lt;/a&gt; (git extension)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/JanDeDobbeleer/oh-my-posh" rel="noopener noreferrer"&gt;Oh-My-Posh&lt;/a&gt; (themes)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/joonro/Get-ChildItemColor" rel="noopener noreferrer"&gt;Get-ChildItemColor&lt;/a&gt; (colorized sub-commands)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/powerline/fonts" rel="noopener noreferrer"&gt;Power Fonts&lt;/a&gt; (techie fonts)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many of these should be installed using &lt;em&gt;PowerShell&lt;/em&gt; with admin rights.&lt;/p&gt;

&lt;p&gt;Well, let's start  💪 💻&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing the ExecutionPolicy
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;NOTE:&lt;/em&gt; this steps should be executed on &lt;em&gt;PowerShell&lt;/em&gt; and &lt;em&gt;PowerShell Core&lt;/em&gt; if you want to have the same theme on both.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, we open the &lt;em&gt;PowerShell&lt;/em&gt; as Admin and change the &lt;code&gt;ExecutionPolicy&lt;/code&gt; to &lt;code&gt;RemoteSigned&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

Set-ExecutionPolicy RemoteSigned &lt;span class="nt"&gt;-Scope&lt;/span&gt; CurrentUser &lt;span class="nt"&gt;-Confirm&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When you are done, you can go back to the previous configuration by updating the &lt;code&gt;ExecutionPolicy&lt;/code&gt; with scope &lt;code&gt;Process&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing what we need
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

PowerShellGet&lt;span class="se"&gt;\I&lt;/span&gt;nstall-Module posh-git &lt;span class="nt"&gt;-Scope&lt;/span&gt; CurrentUser &lt;span class="nt"&gt;-Force&lt;/span&gt;
Install-Module oh-my-posh &lt;span class="nt"&gt;-Scope&lt;/span&gt; CurrentUser
Install-Module &lt;span class="nt"&gt;-AllowClobber&lt;/span&gt; Get-ChildItemColor


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you are using &lt;em&gt;PowerShell Core&lt;/em&gt; you have to install &lt;code&gt;PSReadLine&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

Install-Module &lt;span class="nt"&gt;-Name&lt;/span&gt; PSReadLine &lt;span class="nt"&gt;-Scope&lt;/span&gt; CurrentUser &lt;span class="nt"&gt;-Force&lt;/span&gt; &lt;span class="nt"&gt;-SkipPublisherCheck&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To allow Power Fonts on our system we should clone the Github repository and run.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

.&lt;span class="se"&gt;\i&lt;/span&gt;nstall.ps1


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Tip:&lt;/em&gt; the repo has a lot of fonts, so in my case I deleted all of them except the one called &lt;code&gt;Meslo Dotted&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Allowing all this stuff
&lt;/h2&gt;

&lt;p&gt;In order to have all the modules installed every time we open the terminal, first we need to update our profile file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

code &lt;span class="nv"&gt;$profile&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And add these lines to it&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Modules import&lt;/span&gt;
Import-Module posh-git
Import-Module oh-my-posh
Import-Module Get-ChildItemColor
​
&lt;span class="c"&gt;# Theme to use&lt;/span&gt;
Set-Theme Agnoster


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, we need to tell &lt;em&gt;Windows Terminal&lt;/em&gt; to use our selected font. To do so, open the Settings file&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fl5egjtsofp33qr5wun5t.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fl5egjtsofp33qr5wun5t.jpg" alt="Settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We search for the &lt;em&gt;"profile"&lt;/em&gt; property and update it with the following.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

…..   
&lt;span class="s2"&gt;"profiles"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="s2"&gt;"defaults"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
     &lt;span class="s2"&gt;"fontFace"&lt;/span&gt;: &lt;span class="s2"&gt;"Meslo LG M DZ for Powerline"&lt;/span&gt;,
     &lt;span class="s2"&gt;"fontSize"&lt;/span&gt;: 8,
     &lt;span class="s2"&gt;"colorScheme"&lt;/span&gt; : &lt;span class="s2"&gt;"One Half Dark"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
   &lt;span class="s2"&gt;"list"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
       // Make changes here to the powershell.exe profile
       &lt;span class="s2"&gt;"guid"&lt;/span&gt;: &lt;span class="s2"&gt;"{61c54bbd-c2c6-5271-96e7-009a87ff44bf}"&lt;/span&gt;,
       &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"Windows PowerShell"&lt;/span&gt;,
       &lt;span class="s2"&gt;"commandline"&lt;/span&gt;: &lt;span class="s2"&gt;"powershell.exe"&lt;/span&gt;,
       &lt;span class="s2"&gt;"hidden"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    .....   


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you are using &lt;em&gt;ConEmu&lt;/em&gt; o &lt;em&gt;CMDER&lt;/em&gt; go to the &lt;code&gt;Settings Fonts&lt;/code&gt; and select the Font previously installed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcs1xs0rbm7vfymq7aoc0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcs1xs0rbm7vfymq7aoc0.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;NOTE:&lt;/em&gt; This changes will be reflected on the &lt;em&gt;Visual Studio Code Terminal&lt;/em&gt; too 😉&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Extending this to WSL
&lt;/h1&gt;

&lt;p&gt;The magic of &lt;em&gt;WSL&lt;/em&gt; is that we can use many flavours of Linux directly on Windows, removing any need of double-boot or &lt;em&gt;VMs&lt;/em&gt;. Even so, its bash is still boring! so let's see how we can have the same here.&lt;/p&gt;

&lt;p&gt;The natural option is to install PowerShell Core on the distro and set it as default bash, in order to follow the same steps as previously.&lt;/p&gt;

&lt;p&gt;But, we are on Linux, where there are many alternatives and a good one is &lt;em&gt;ZSH&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;First we'll update our Linux version&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get upgrade


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now we can install everything we need&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;git
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;zsh


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We make &lt;em&gt;ZSH&lt;/em&gt; our default&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

chsh &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/zsh


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Update the &lt;code&gt;.bashrc&lt;/code&gt; file by adding this at the beginning of it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; 1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
&lt;/span&gt;&lt;span class="nb"&gt;exec &lt;/span&gt;zsh
Fi


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Install &lt;a href="https://ohmyz.sh/" rel="noopener noreferrer"&gt;Oh-My-Zsh&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We select the &lt;a href="https://ohmyz.sh/" rel="noopener noreferrer"&gt;theme&lt;/a&gt; from the &lt;code&gt;./zshrc&lt;/code&gt; file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

code &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.zshrc


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Search for “ZSH_THEME” property and change it for one you like&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;ZSH_THEME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"norm"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The last step is to install the plugin &lt;a href="https://github.com/joel-porquet/zsh-dircolors-solarized" rel="noopener noreferrer"&gt;zsh-dircolors-solarized&lt;/a&gt; by cloning the repo&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

git clone &lt;span class="nt"&gt;--recursive&lt;/span&gt; git://github.com/joel-porquet/zsh-dircolors-solarized &lt;span class="nv"&gt;$ZSH_CUSTOM&lt;/span&gt;/plugins/zsh-dircolors-solarized


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then go back to the &lt;code&gt;.zshrc&lt;/code&gt; file and grant permissions to the pluging&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;zsh-dircolors-solarized&lt;span class="o"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Finally select the theme for the plugin&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

setupsolarized dircolors.ansi-dark


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The result…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frkeuql4ud5nuhs7obozo.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frkeuql4ud5nuhs7obozo.JPG" alt="Result"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Extending this to Azure Cloud Shell
&lt;/h1&gt;

&lt;p&gt;I didn't try to set up all these things on &lt;em&gt;Azure Cloud Shell&lt;/em&gt;, but it's based on Linux - so you can install ZSH and set it up as we did! Here it's explained (&lt;a href="https://www.danielstechblog.io/setting-up-zsh-with-oh-my-zsh-in-azure-cloud-shell/)%5Bhttps://www.danielstechblog.io/setting-up-zsh-with-oh-my-zsh-in-azure-cloud-shell/%5D" rel="noopener noreferrer"&gt;https://www.danielstechblog.io/setting-up-zsh-with-oh-my-zsh-in-azure-cloud-shell/)[https://www.danielstechblog.io/setting-up-zsh-with-oh-my-zsh-in-azure-cloud-shell/]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6wsps87yuia3kxofhyxj.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6wsps87yuia3kxofhyxj.JPG" alt="Well done!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🧉 + 🥐🥐 = Happy coder 💻&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Kafka + Docker + Net Core 101 - parte 1</title>
      <dc:creator>Andres Lozada Mosto</dc:creator>
      <pubDate>Wed, 10 Jun 2020 21:33:52 +0000</pubDate>
      <link>https://dev.to/cloudx/kafka-docker-net-core-101-parte-1-5gcf</link>
      <guid>https://dev.to/cloudx/kafka-docker-net-core-101-parte-1-5gcf</guid>
      <description>&lt;p&gt;Updates&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;20/07/2020: Actualizado para May 2020 Windows Update y Docker Desktop sobre WSL2.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Si llegaste hasta este lugar es que ya sabes de lo que estamos hablando y quieres meter manos en el código de forma inmediata entonces esta serie de artículos es para vos 😀&lt;/p&gt;

&lt;p&gt;Si aún no tienes bien en claro qué es &lt;strong&gt;Kafka&lt;/strong&gt; y para que se lo puede usar puedes pasarte por estos lados:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="https://medium.com/@sonusharma.mnnit/apache-kafka-in-depth-49aae1e844be" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F-dPKGsx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/fit/c/56/56/1%2A0DC89DfVpGHJ-Gj3AFCXvQ.jpeg" alt="Sonu Sharma"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://medium.com/@sonusharma.mnnit/apache-kafka-in-depth-49aae1e844be" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Apache Kafka in Depth. In the era of Big Data, lots and lots… | by Sonu Sharma | Medium&lt;/h2&gt;
      &lt;h3&gt;Sonu Sharma ・ &lt;time&gt;Jul 24, 2019&lt;/time&gt; ・ 9 min read
      &lt;div class="ltag__link__servicename"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KBvj_QRD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/medium_icon-90d5232a5da2369849f285fa499c8005e750a788fdbf34f5844d5f2201aae736.svg" alt="Medium Logo"&gt;
        Medium
      &lt;/div&gt;
    &lt;/h3&gt;
&lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;div class="ltag__link"&gt;
  &lt;a href="https://medium.com/@rinu.gour123/kafka-for-beginners-74ec101bc82d" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pW2khHsr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/fit/c/56/56/1%2AwUv5jHLVJnmUjE6vbQjF6Q.jpeg" alt="Rinu Gour"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://medium.com/@rinu.gour123/kafka-for-beginners-74ec101bc82d" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Kafka For Beginners. What is Kafka? | by Rinu Gour | Medium&lt;/h2&gt;
      &lt;h3&gt;Rinu Gour ・ &lt;time&gt;Sep 12, 2018&lt;/time&gt; ・ 9 min read
      &lt;div class="ltag__link__servicename"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KBvj_QRD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/medium_icon-90d5232a5da2369849f285fa499c8005e750a788fdbf34f5844d5f2201aae736.svg" alt="Medium Logo"&gt;
        Medium
      &lt;/div&gt;
    &lt;/h3&gt;
&lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Bueno, ahora ya sabemos para que podemos usar Kafka tratemos de correrlo localmente en nuestra PC, probar comandos básicos y crear una aplicación básica que publique eventos y otra que los consuma utilizando &lt;strong&gt;Net Core&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Kafka + Docker
&lt;/h1&gt;

&lt;p&gt;Para levantar nuestro ambiente creamos un archivo de Docker Compose donde instanciaremos un servicio de &lt;em&gt;Zookeeper&lt;/em&gt; y un servicio de &lt;em&gt;Kafka&lt;/em&gt; (luego se pueden levantar mas y armar los clusters).&lt;/p&gt;

&lt;p&gt;Las imágenes base que vamos a utilizar son las de los amigos de &lt;em&gt;&lt;a href="https://github.com/confluentinc/cp-docker-images"&gt;Confluence&lt;/a&gt;&lt;/em&gt;. No son las únicas, también podemos usar las de &lt;em&gt;&lt;a href="https://bitnami.com/stack/zookeeper/containers"&gt;Bitmani|Zookeeper&lt;/a&gt;&lt;/em&gt;, &lt;em&gt;&lt;a href="https://bitnami.com/stack/kafka/containers"&gt;Bitmani|Kafka&lt;/a&gt;&lt;/em&gt;, &lt;em&gt;&lt;a href="https://hub.docker.com/r/spotify/kafka/"&gt;Spotify&lt;/a&gt;&lt;/em&gt; o &lt;em&gt;&lt;a href="https://github.com/wurstmeister/kafka-docker"&gt;Wurstmeister&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Nuestro docker-compose file nos queda de esta forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '2'
services:
    zookeeper:
        image: confluentinc/cp-zookeeper:latest
        environment:
            ZOOKEEPER_CLIENT_PORT: 2181
            ZOOKEEPER_TICK_TIME: 2000
    kafka:
        image: confluentinc/cp-kafka:latest
        depends_on:
            - zookeeper
        ports:
            - 9092:9092
        environment:
            KAFKA_BROKER_ID: 1
            KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
            KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://127.0.0.1:9092
            KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
            KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
            KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Imporatante:&lt;/strong&gt; Para acceder a &lt;em&gt;Kafka&lt;/em&gt; desde fuera del container se debe reeamplazar &lt;code&gt;localhost&lt;/code&gt; en &lt;em&gt;ADVERTISED&lt;/em&gt; por el IP del Docker-Machine.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Levantando el ambiente con Docker Tools (windows)
&lt;/h2&gt;

&lt;p&gt;Abrimos la consola de docker y creamos una nueva &lt;em&gt;Docker Machine&lt;/em&gt; ejecutando el siguiente comando&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-machine create &lt;span class="nt"&gt;--driver&lt;/span&gt; virtualbox confluent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Indicamos que vamos a utilizar el driver de &lt;em&gt;VirtualBox&lt;/em&gt; para crear la máquina virtual del host y la llamaremos &lt;em&gt;confluent&lt;/em&gt; (podemos usar cualquier nombre)&lt;/p&gt;

&lt;p&gt;Si usamos Linux o Windows Profesional en adelante, podemos saltarnos esta opción e ir directamente al &lt;code&gt;docker-compose up&lt;/code&gt;, pero para quienes usamos Windows 10 Home o versiones mas antiguas con Docker Toolbox es un paso que necesitamos hacer para tener una experiencia similar.&lt;/p&gt;

&lt;p&gt;Revisamos que se haya creado&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-machine &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Debemos ver algo similar a esto&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TIfET1KY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zs27km03s4aleugf1bxk.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TIfET1KY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zs27km03s4aleugf1bxk.PNG" alt="doker-machine ls"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lo siguiente a realizar es entrar a la &lt;em&gt;docker machine&lt;/em&gt; que acabamos de crear para que los comandos se ejecuten sobre ella (sino se haría sobre la default)&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;docker-machine &lt;span class="nb"&gt;env &lt;/span&gt;confluent&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Obtenemos el IP para luego poder conectarnos desde &lt;em&gt;C#&lt;/em&gt; y para usarla en la variable de entorno &lt;em&gt;KAFKA_ADVERTISED_LISTENERS&lt;/em&gt; del archivo de docker compose&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-machine ip confluent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Por último, corremos &lt;code&gt;docker-compose&lt;/code&gt; en la carpeta donde hayamos puesto el archivo yaml&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Verificamos que se encuentren Kafka y Zookeeper levantados&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Levantando el ambiente en Linux o con Docker for Windows
&lt;/h2&gt;

&lt;p&gt;Simplemente corremos Docker Compose&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Si estas usando WSL puedes obtener el IP de la siguiente forma&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsl ip -a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Con esta mínima configuración tenemos &lt;em&gt;Kafka&lt;/em&gt; listo para las primeras pruebas, let's go! 💪😀&lt;/p&gt;


&lt;h1&gt;
  
  
  Divirtámonos con Kafka
&lt;/h1&gt;

&lt;p&gt;Por default, &lt;strong&gt;Kafka&lt;/strong&gt; viene con scrips para crear topics, publicar/leer mensajes, etc. Durante esta parte, vamos a ver como trabajar con ellos&lt;/p&gt;
&lt;h2&gt;
  
  
  Crear un topic nuevo
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container &lt;span class="nb"&gt;exec&lt;/span&gt; &amp;lt;kafka-container-id&amp;gt; kafka-topics &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--create&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--topic&lt;/span&gt; foo &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--partitions&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--replication-factor&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--if-not-exists&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--zookeeper&lt;/span&gt; zookeeper:2181
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Creamos un nuevo &lt;em&gt;Topic&lt;/em&gt; en &lt;em&gt;Kafka&lt;/em&gt; con una factor de replicación de 1 y con una sola partición. Si quisiéramos aumentar estos valores debemos levantar otras instancias de Kafka.&lt;br&gt;
El parámetro &lt;em&gt;--if-not-exists&lt;/em&gt; indica que el topic se creará si no existe previamente.&lt;/p&gt;
&lt;h3&gt;
  
  
  Listar los topics
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container &lt;span class="nb"&gt;exec&lt;/span&gt; &amp;lt;kafka-container-id&amp;gt; kafka-topics &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--list&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--zookeeper&lt;/span&gt; zookeeper:2181
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Este comando va a listar únicamente los nombres de los &lt;em&gt;Topics&lt;/em&gt; que tenemos creados&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HOeXZyti--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/mzsz90i9y72tbws6f3bu.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HOeXZyti--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/mzsz90i9y72tbws6f3bu.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Ver la descripción de un topic
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container &lt;span class="nb"&gt;exec&lt;/span&gt; &amp;lt;kafka-container-id&amp;gt; kafka-topics &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--describe&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--topic&lt;/span&gt; foo &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--zookeeper&lt;/span&gt; zookeeper:2181
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Nos muestra información relevante del Topic&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DE6okWEO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/aiaxxozrh3ih9e8v2lq5.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DE6okWEO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/aiaxxozrh3ih9e8v2lq5.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Publicar un mensaje al topic
&lt;/h2&gt;

&lt;p&gt;En este caso es mejor ingresar a la consola del container de Kafka y ejecutar el Producer desde allí&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;kafka-container-id&amp;gt; /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Ahora ejecutamos el &lt;em&gt;Publisher&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kafka-console-producer &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--request-required-acks&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--broker-list&lt;/span&gt; &amp;lt;docker-machine-ip&amp;gt;:9092 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--topic&lt;/span&gt; foo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Luego por cada línea que escribamos (separadas por un [Enter]) se enviará un mensaje (usamos [ctrl+c] para salir del comando y exit para salir del container).&lt;/p&gt;
&lt;h2&gt;
  
  
  Leer de un topic
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container &lt;span class="nb"&gt;exec&lt;/span&gt; &amp;lt;kafka-container-id&amp;gt; kafka-console-consumer &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--bootstrap-server&lt;/span&gt; &amp;lt;docker-machine-ip&amp;gt;:9092 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--topic&lt;/span&gt; foo &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--zookeeper&lt;/span&gt;  zookeeper:2181
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Estos son los básicos para comenzar y hacer las primeras pruebas. Cada uno tiene múltiples parámetros a investigar y sacar mayor provecho.&lt;br&gt;
Para un major detalle pueden ir a la docu &lt;a href="https://kafka.apache.org/documentation/#operations"&gt;oficial&lt;/a&gt;.&lt;/p&gt;


&lt;h1&gt;
  
  
  C# conoce Kafka
&lt;/h1&gt;

&lt;p&gt;Ya tenemos &lt;strong&gt;Kafka&lt;/strong&gt; ejecutándose en nuestro PC, ahora veremos como publicar y leer mensajes en un &lt;em&gt;Topic&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Producer&lt;/strong&gt;&lt;br&gt;
Creamos una aplicación de consola y usamos el siguiente código reemplazando el &lt;em&gt;&lt;/em&gt; por el que tiene el &lt;em&gt;Docker-Machine&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Compilamos y ejecutamos indicando el nombre del topic al cual enviaremos mensajes. La aplicación queda a la espera de que escribamos mensajes uno por línea.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet build
&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
dotnet run foo

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Consumer&lt;/strong&gt;&lt;br&gt;
Para el Consumer, creamos una nueva aplicación de consola y usamos el siguiente código reemplazando el valor de &lt;em&gt;&lt;/em&gt; por el IP del &lt;em&gt;Docker-Machine&lt;/em&gt; que estamos usando.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Compilamos y corremos indicando que &lt;em&gt;Topic&lt;/em&gt; queremos escuchar&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet build
&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
dotnet run foo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ejecutando ambos programas en la consola una al lado de la otra, podremos ver que cuando publicamos un mensaje, éste aparece en la otra.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sH8Vvlrv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rj2ku5r6xnevrtvjt5g5.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sH8Vvlrv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rj2ku5r6xnevrtvjt5g5.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pueden bajarse el código desde &lt;a href="https://github.com/andreslozada/net-thoughts/tree/master/kafka/examples/parte1"&gt;acá&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Kafka tools
&lt;/h1&gt;

&lt;p&gt;El universo de Tools de Kafka es inmenso y en estos listados se puede encontrar de todo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/monksy/awesome-kafka"&gt;https://github.com/monksy/awesome-kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/semantalytics/awesome-kafka"&gt;https://github.com/semantalytics/awesome-kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dharmeshkakadia/awesome-kafka#testing"&gt;https://github.com/dharmeshkakadia/awesome-kafka#testing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hay varias herramientas para manejar &lt;em&gt;Kafka&lt;/em&gt; por medio de UI y no por comandos&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.conduktor.io/"&gt;Conduktor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.kafkatool.com/"&gt;KafkaTool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/obsidiandynamics/kafdrop"&gt;Kafdrop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hay otros dando vueltas por ahí...&lt;/p&gt;

&lt;p&gt;Entre los que probe, &lt;em&gt;Conduktor&lt;/em&gt; parece bastante completo aunque la version free tiene sus limitantes. Lo bueno es que tiene integración para conectarse a &lt;em&gt;Confluent&lt;/em&gt; y &lt;em&gt;Aiven&lt;/em&gt; para usar sus &lt;em&gt;plataformas SaaS de Kafka&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7S-p6Smx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7zyszz9k299wuqlq4se0.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7S-p6Smx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7zyszz9k299wuqlq4se0.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Cerrando
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Kafka&lt;/strong&gt; es un monstruo que nos provee un nuevo universo de posibilidades para extender nuestras arquitecturas de maneras impensadas pero no hay que tenerle miedo para comenzar.&lt;/p&gt;

&lt;p&gt;Vimos que con muy poco (gracias a los que crearon las imágenes de docker) se puede tener todo lo necesario para tener a Kafka corriendo sobre docker en nuestra PC y lo fácil que es comunicarse desde C#.&lt;/p&gt;

&lt;p&gt;En las futuras entradas de esta serie de artículos iremos viendo como ir avanzando un poco más.&lt;/p&gt;

&lt;p&gt;🧉 + 🥐🥐 = Happy coder 💻&lt;/p&gt;

</description>
      <category>kafka</category>
      <category>dotnetcore</category>
      <category>aspnetcore</category>
      <category>csharp</category>
    </item>
  </channel>
</rss>
