<?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: Davide Bellone</title>
    <description>The latest articles on DEV Community by Davide Bellone (@bellonedavide).</description>
    <link>https://dev.to/bellonedavide</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%2F147708%2F9fbda111-af80-4cdd-8a1c-1b4d47e9f088.gif</url>
      <title>DEV Community: Davide Bellone</title>
      <link>https://dev.to/bellonedavide</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bellonedavide"/>
    <language>en</language>
    <item>
      <title>HTTP Logging in ASP.NET: how to automatically log all incoming HTTP requests (and its downsides!)</title>
      <dc:creator>Davide Bellone</dc:creator>
      <pubDate>Mon, 23 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/bellonedavide/http-logging-in-aspnet-how-to-automatically-log-all-incoming-http-requests-and-its-downsides-1248</link>
      <guid>https://dev.to/bellonedavide/http-logging-in-aspnet-how-to-automatically-log-all-incoming-http-requests-and-its-downsides-1248</guid>
      <description>&lt;p&gt;Whenever we publish a service, it is important to add proper logging to the application. &lt;strong&gt;Logging helps us understand how the system works and behaves&lt;/strong&gt;, and it's a fundamental component that enables us to troubleshoot problems that occur during application use.&lt;/p&gt;

&lt;p&gt;In this blog, we have discussed logging several times. However, we mostly focused on manually written logs.&lt;/p&gt;

&lt;p&gt;In this article, we will learn how to log incoming HTTP requests to help us understand how our APIs are being used from the outside.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaffolding the empty project
&lt;/h2&gt;

&lt;p&gt;To showcase this type of logging, I created an ASP.NET API. It's a very simple application with CRUD operations on an in-memory collection.&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="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&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;"[controller]"&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;BooksController&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;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;booksCatalogue&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;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&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;index&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Book&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;index&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;$"Book with ID &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;index&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="nf"&gt;ToList&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;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BooksController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&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;BooksController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BooksController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&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;These CRUD operations are exposed via HTTP APIs, following the usual verb-based convention.&lt;/p&gt;

&lt;p&gt;For example:&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;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&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;Book&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetBook&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;FromRoute&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;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Looking if in my collection with {TotalBooksCount} books there is one with ID {SearchedId}"&lt;/span&gt;
            &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;booksCatalogue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&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;Book&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;booksCatalogue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SingleOrDefault&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;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;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="k"&gt;switch&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;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&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;book&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;As you can see, I have added some custom logs: before searching for the element with the specified ID, I also wrote a log message such as "Looking if in my collection with 5 books there is one with ID 2".&lt;/p&gt;

&lt;p&gt;Where can I find the message? For the sake of this article, I decided to use Seq!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Seq is a popular log sink&lt;/strong&gt; (well, as you may know, my favourite one!), that is easy to install and to integrate with .NET. I've thoroughly explained how to use Seq in conjunction with ASP.NET in &lt;a href="https://www.code4it.dev/blog/logging-with-ilogger-and-seq/" rel="noopener noreferrer"&gt;this article&lt;/a&gt; and in other ones.&lt;/p&gt;

&lt;p&gt;In short, the most important change in your application is to add Seq as the log sink, like this:&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;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddLogging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lb&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;lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSeq&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;Now, whenever I call the GET endpoint, I can see the related log messages appear in Seq:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fyxfdc6oka1czkjbl3dh2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fyxfdc6oka1czkjbl3dh2.png" alt="Custom log messages" width="771" height="83"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But sometimes it's not enough. I want to see more details, and I want them to be applied everywhere!&lt;/p&gt;

&lt;h2&gt;
  
  
  How to add HTTP Logging to an ASP.NET application
&lt;/h2&gt;

&lt;p&gt;HTTP Logging is a way of logging most of the details of the incoming HTTP operations, &lt;strong&gt;tracking both the requests and the responses&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With HTTP Logging, you don't need to manually write custom logs to access the details of incoming requests: you just need to add its related middleware, configure it as you want, and have all the required logs available for all your endpoints.&lt;/p&gt;

&lt;p&gt;Adding it is pretty straightforward: you first need to add the &lt;code&gt;HttpLogging&lt;/code&gt; middleware to the list of services:&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;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHttpLogging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lb&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;so that you can use it once the &lt;code&gt;WebApplication&lt;/code&gt; instance is built:&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;UseHttpLogging&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's still a problem, though: &lt;strong&gt;all the logs generated via HttpLogging are, by default, ignored&lt;/strong&gt;, as logs coming from their namespace (named &lt;code&gt;Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware&lt;/code&gt;) are at &lt;em&gt;Information&lt;/em&gt; log level, thus ignored because of the default configurations.&lt;/p&gt;

&lt;p&gt;You either have to update the &lt;code&gt;appsetting.json&lt;/code&gt; file to &lt;strong&gt;tell the logging system to process logs from that namespace&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Logging"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"LogLevel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Information"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Microsoft.AspNetCore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Warning"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Information"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or, alternatively, you need to do the same when setting up the logging system in the Program class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;builder.Services.AddLogging(lb =&amp;gt; {
&lt;/span&gt;  lb.AddSeq();
&lt;span class="gi"&gt;+ lb.AddFilter("Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware", LogLevel.Information);
&lt;/span&gt;});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then have all our pieces in place: let's execute the application!&lt;/p&gt;

&lt;p&gt;First, you can spin up the API; you should be able to see the Swagger page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Faqjujrkkhtdnv1wb0gdl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Faqjujrkkhtdnv1wb0gdl.png" alt="Swagger page for our application's API" width="800" height="573"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here, you can call the GET endpoint:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F0pdn1ac49h9txyvs5s7n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F0pdn1ac49h9txyvs5s7n.png" alt="Http response of the API call, as seen on Swagger" width="667" height="572"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should now be able to see all the logs in Seq:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Flwxx6uyikd69yd3yiw8y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Flwxx6uyikd69yd3yiw8y.png" alt="Logs list in Seq" width="800" height="43"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see from the screenshot above, I have a log entry for the request and one for the response. Also, of course, I have the custom message I added manually in the C# method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding HTTP Request logs
&lt;/h3&gt;

&lt;p&gt;Let's focus on the data logged for the HTTP request.&lt;/p&gt;

&lt;p&gt;If we open the log related to the HTTP request, we can see all these values:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Flb1hss4ozi8r0k0u1h7s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Flb1hss4ozi8r0k0u1h7s.png" alt="Details of the HTTP Request" width="650" height="819"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Among these details, we can see properties such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the host name (&lt;em&gt;localhost:7164&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;the method (&lt;em&gt;GET&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;the path (&lt;em&gt;/books/4&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and much more.&lt;/p&gt;

&lt;p&gt;You can see all the properties as standalone items, but you can also have a grouped view of all the properties by accessing the &lt;code&gt;HttpLog&lt;/code&gt; element:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fg8imqfuh1uiestb5qu9x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fg8imqfuh1uiestb5qu9x.png" alt="Details of the HTTP Log element" width="477" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that for some elements we do not have access to the actual value, as &lt;strong&gt;the value is set to &lt;code&gt;[Redacted]&lt;/code&gt;&lt;/strong&gt;. This is a default configuration that prevents logging too many things (and undisclosing some values) as well as writing too much content on the log sink (the more you write, the less performant the queries become - and you also pay more!).&lt;/p&gt;

&lt;p&gt;Among other redacted values, you can see that even the Cookie value is not directly available - for the same reasons explained before.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding HTTP Response logs
&lt;/h3&gt;

&lt;p&gt;Of course, we can see some interesting data in the Response log:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fltc9jj26q99e93lm1hkg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fltc9jj26q99e93lm1hkg.png" alt="Details of the HTTP Response" width="635" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, among some other properties such as the &lt;em&gt;Host Name&lt;/em&gt;, we can see the &lt;em&gt;Status Code&lt;/em&gt; and the &lt;em&gt;Trace Id&lt;/em&gt; (which, as you may notice, is the same as the one in the Request).&lt;/p&gt;

&lt;p&gt;As you can see, the log item does not contain the body of the response.&lt;/p&gt;

&lt;p&gt;Also, just as it happens with the Request, we do not have access to the list of HTTP Headers.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to save space, storage, and money by combining log entries
&lt;/h2&gt;

&lt;p&gt;For every HTTP operation, we end up with 2 log entries: one for the Request and one for the Response.&lt;/p&gt;

&lt;p&gt;However, it would be more practical to &lt;strong&gt;have both request and response info stored in the same log item&lt;/strong&gt; so we can understand more easily what is happening.&lt;/p&gt;

&lt;p&gt;Lucky for us, this functionality is already in place. We just need to set the &lt;code&gt;CombineLogs&lt;/code&gt; property to &lt;code&gt;true&lt;/code&gt; when we add the HttpLogging functionality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;builder.Services.AddHttpLogging(lb =&amp;gt;
&lt;/span&gt;{
&lt;span class="gi"&gt;+  lb.CombineLogs = true;
&lt;/span&gt;}
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we are able to see the data for both the request and the related response in the same log element.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F9pjga94szgp4vfs0872q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F9pjga94szgp4vfs0872q.png" alt="Request and Response combined logs" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The downsides of using HTTP Logging
&lt;/h2&gt;

&lt;p&gt;Even though everything looks nice and pretty, adding HTTP Logging has some serious consequences.&lt;/p&gt;

&lt;p&gt;First of all, remember that you are doing some more operations for every incoming HTTP request. Just processing and storing the log messages can bring to an &lt;strong&gt;application performance downgrade&lt;/strong&gt; - you are using parts of the processing resources to interpret the HTTP context, create the correct log entry, and store it.&lt;/p&gt;

&lt;p&gt;Depending on how your APIs are structured, you may need to &lt;strong&gt;strip out sensitive data&lt;/strong&gt;: HTTP Logs, by default, log almost everything (except for the parts stored as Redacted). Since you don't want to store the content of the requests as plain text, you may need to create custom logic to redact parts of the request and response you want to hide: you may need to implement a &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-logging/?view=aspnetcore-9.0&amp;amp;wt.mc_id=DT-MVP-5005077#ihttplogginginterceptor" rel="noopener noreferrer"&gt;custom IHttpLoggingInterceptor&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, consider that logging occupies storage, and storage has a cost. &lt;strong&gt;The more you log, the higher the cost&lt;/strong&gt;. You should define proper strategies to avoid excessive storage costs while keeping valuable logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further readings
&lt;/h2&gt;

&lt;p&gt;There is a lot more, as always. In this article, I focused on the most essential parts, but the road to having proper HTTP Logs is still long.&lt;/p&gt;

&lt;p&gt;You may want to start from the official documentation, of course!&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-logging/?view=aspnetcore-9.0&amp;amp;wt.mc_id=DT-MVP-5005077" rel="noopener noreferrer"&gt;HTTP logging in ASP.NET Core | Microsoft Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article first appeared on &lt;a href="https://www.code4it.dev/" rel="noopener noreferrer"&gt;Code4IT 🐧&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;All the logs produced for this article were stored on Seq. You can find more info about installing and integrating Seq in ASP.NET Core in this article:&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://www.code4it.dev/blog/logging-with-ilogger-and-seq/" rel="noopener noreferrer"&gt;Easy logging management with Seq and ILogger in ASP.NET | Code4IT&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;HTTP Logging can be a good tool for understanding the application behaviour and detecting anomalies. However, as you can see, there are some important downsides that need to be considered.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>logging</category>
      <category>api</category>
      <category>http</category>
    </item>
    <item>
      <title>Fitness Functions in Software Architecture: measuring things to ensure prosperity</title>
      <dc:creator>Davide Bellone</dc:creator>
      <pubDate>Thu, 19 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/bellonedavide/fitness-functions-in-software-architecture-measuring-things-to-ensure-prosperity-56p3</link>
      <guid>https://dev.to/bellonedavide/fitness-functions-in-software-architecture-measuring-things-to-ensure-prosperity-56p3</guid>
      <description>&lt;p&gt;Just creating an architecture is not enough; you should also make sure that the pieces you are building are, in the end, the ones your system needs.&lt;/p&gt;

&lt;p&gt;Is your system fast enough? Is it passing all the security checks? What about testability, maintainability, and other &lt;em&gt;-ilities&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;Fitness Functions are components of the architecture that do not execute functional operations, but, using a set of tests and measurements, allow you to validate that the system respects all the non-functional requirements defined upfront.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fitness Functions: because non-functional requirements matter
&lt;/h2&gt;

&lt;p&gt;An architecture is made of two main categories of requirements: &lt;em&gt;functional&lt;/em&gt; requirements and &lt;em&gt;non-functional&lt;/em&gt; requirements.&lt;/p&gt;

&lt;p&gt;Functional requirements are the easiest to define and test: if one of the requirements is "a user with role Admin must be able to see all data", then writing a suite of tests for this requirement is straightforward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Non-functional requirements are, for sure, as important as functional requirements&lt;/strong&gt;, but are often overlooked or not detailed. "The system must be fast": ok, &lt;em&gt;how fast&lt;/em&gt;? What do you mean by "fast"? What is an acceptable value of "fast"?&lt;/p&gt;

&lt;p&gt;If we don't have a clear understanding of non-functional requirements, then it's impossible to measure them.&lt;/p&gt;

&lt;p&gt;And once we have defined a way to measure them, how can we ensure that we are meeting our expectations? Here's where Fitness Functions come in handy.&lt;/p&gt;

&lt;p&gt;In fact, Fitness Functions are specific components that focus on non-functional requirements, executing some calculations and providing metrics that help architects and developers ensure that the system's architecture aligns with business goals, technical requirements, and other quality attributes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Fitness Functions are crucial for future-proof architectures
&lt;/h2&gt;

&lt;p&gt;When creating an architecture, you must think of the most important &lt;em&gt;-ilities&lt;/em&gt; for that specific case. How can you ensure that the technical choices we made meet the expectations?&lt;/p&gt;

&lt;p&gt;By being related to specific and measurable metrics, Fitness Functions provide a way to assess the architecture's quality and performance, reducing the reliance on subjective opinions by using objective measurements. A metric can be a simple number (e.g., "maximum number of requests per second"), a percentage value (like "percentage of code covered by tests") or other values that are still measurable.&lt;/p&gt;

&lt;p&gt;Knowing how the system behaves with respect to these measures allows architects to work on continuous improvement: teams can &lt;strong&gt;identify areas for improvement and make decisions based not on personal opinion but on actual data to enhance the system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Having a centralised place to view the historical values of a measure helps understand if you have made progress or, as time goes by, the quality has degraded.&lt;/p&gt;

&lt;p&gt;Still talking about the historical values of the measures, having a clear understanding of what is the current status of such metrics can help in identifying potential issues early in the development process, allowing teams to address them before they become critical problems.&lt;/p&gt;

&lt;p&gt;For example, by using Fitness Functions, you can ensure that the system is able to handle a certain number of users per second: having proper measurements, you can identify which functionalities are less performant and, in case of high traffic, may bring the whole system down.&lt;/p&gt;

&lt;h2&gt;
  
  
  You are already using Fitness Functions, but you didn't know
&lt;/h2&gt;

&lt;p&gt;Fitness Functions sound like complex things to handle.&lt;/p&gt;

&lt;p&gt;Even though &lt;strong&gt;you can create your own functions&lt;/strong&gt;, most probably you are already using them without knowing it. Lots of tools are available out there that cover several metrics, and I'm sure you've already used some of them (or, at least, you've already heard of them).&lt;/p&gt;

&lt;p&gt;Tools like &lt;a href="https://docs.sonarsource.com/sonarqube-community-build/" rel="noopener noreferrer"&gt;SonarQube&lt;/a&gt; and &lt;a href="https://www.ndepend.com/" rel="noopener noreferrer"&gt;NDepend&lt;/a&gt; use Fitness Functions to evaluate code quality based on metrics such as code complexity, duplication, and adherence to coding standards. Those metrics are calculated based on static analysis of the code, and teams can define thresholds under which a system can be at risk of losing maintainability. &lt;strong&gt;An example of a metric related to code quality is Code Coverage&lt;/strong&gt;: the higher, the better (even though &lt;a href="https://www.code4it.dev/blog/code-coverage-must-not-be-the-target/" rel="noopener noreferrer"&gt;100% of code coverage does not guarantee your code is healthy&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Tools like &lt;a href="https://jmeter.apache.org/" rel="noopener noreferrer"&gt;JMeter&lt;/a&gt; or &lt;a href="https://k6.io/" rel="noopener noreferrer"&gt;K6&lt;/a&gt; help you measure system performance under various conditions. Having a history of load testing results can help ensure that, as you add new functionality to the system, the performance of specific modules does not degrade.&lt;/p&gt;

&lt;p&gt;All in all, &lt;strong&gt;most of the Fitness Functions can be set to be part of CI/CD pipelines&lt;/strong&gt;: for example, you can configure a CD pipeline to block the deployment of the code on a specific system if the load testing results of the new code are worse than the previous version. Or you could block a Pull Request if the code coverage percentage is getting lower.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further readings
&lt;/h2&gt;

&lt;p&gt;A good way to start experimenting with Load Testing is by running it locally. A nice open-source project is K6: you can install it on your local machine, define the load phases, and analyse the final result.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://www.code4it.dev/blog/k6-load-testing/" rel="noopener noreferrer"&gt;Getting started with Load testing with K6 on Windows 11 | Code4IT&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article first appeared on &lt;a href="https://www.code4it.dev/" rel="noopener noreferrer"&gt;Code4IT 🐧&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But, even if you don't really care about load testing (maybe because your system is not expected to handle lots of users), I'm sure you still care about code quality and its tests. When using .NET, you can collect code coverage reports using Cobertura. Then, if you are using Azure DevOps, you may want to stop a Pull Request if the code coverage percentage has decreased.&lt;/p&gt;

&lt;p&gt;Here's how to do all of this:&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://www.code4it.dev/blog/code-coverage-on-azure-devops-yaml-pipelines/" rel="noopener noreferrer"&gt;Cobertura, YAML, and Code Coverage Protector: how to view Code Coverage report on Azure DevOps | Code4IT&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Sometimes, there are things we use every day but don't know how to name: Fitness Functions are one of them, and they are the foundation of future-proof software systems.&lt;/p&gt;

&lt;p&gt;You can create your own Fitness Functions based on whatever you can (and need to) measure: from average page loading to star-rated customer satisfaction. In conjunction with a clear dashboard, you can provide a clear view of the history of such metrics.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>fitnessfunction</category>
      <category>requirements</category>
    </item>
    <item>
      <title>C# Tip: 2 ways to generate realistic data using Bogus</title>
      <dc:creator>Davide Bellone</dc:creator>
      <pubDate>Tue, 17 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/bellonedavide/c-tip-2-ways-to-generate-realistic-data-using-bogus-1i4d</link>
      <guid>https://dev.to/bellonedavide/c-tip-2-ways-to-generate-realistic-data-using-bogus-1i4d</guid>
      <description>&lt;p&gt;In a previous article, we delved into the creation of realistic data using Bogus, an open-source library that allows you to generate data with plausible values.&lt;/p&gt;

&lt;p&gt;Bogus contains several properties and methods that generate realistic data, such as names, addresses, birthdays, and so on.&lt;/p&gt;

&lt;p&gt;In this article, we will learn two ways to generate data with Bogus. Both produce the same result; the main difference lies in reusability and modularity. In my opinion, it's mostly a matter of preference: there is no approach &lt;em&gt;absolutely&lt;/em&gt; better than the other, and either approach can be preferable depending on the context.&lt;/p&gt;

&lt;p&gt;For the sake of this article, we are going to use Bogus to generate instances of the &lt;code&gt;Book&lt;/code&gt; class, defined like this:&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&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;Guid&lt;/span&gt; &lt;span class="n"&gt;Id&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="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Title&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="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;PagesCount&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="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Genre&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;Genres&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="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateOnly&lt;/span&gt; &lt;span class="n"&gt;PublicationDate&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="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;AuthorFirstName&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="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;AuthorLastName&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Genre&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Thriller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Fantasy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Romance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Biography&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Expose a Faker inline or with a method
&lt;/h2&gt;

&lt;p&gt;It is possible to create a specific object that, using a Builder approach, allows you to generate one or more items of a specified type.&lt;/p&gt;

&lt;p&gt;It all starts with the &lt;code&gt;Faker&amp;lt;T&amp;gt;&lt;/code&gt; generic type, where &lt;code&gt;T&lt;/code&gt; is the type you want to generate.&lt;/p&gt;

&lt;p&gt;Once you create it, you can define the rules to be used when initializing the properties of a &lt;code&gt;Book&lt;/code&gt; by using methods such as &lt;code&gt;RuleFor&lt;/code&gt; and &lt;code&gt;RuleForType&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BogusBookGenerator&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;static&lt;/span&gt; &lt;span class="n"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CreateFaker&lt;/span&gt;&lt;span class="p"&gt;()&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;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bookFaker&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;Faker&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Book&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;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&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;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&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="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&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;Text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Genres&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EnumValues&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Genre&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;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthorFirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthorLastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PagesCount&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;800&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RuleForType&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;DateOnly&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PastDateOnly&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;bookFaker&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;In this way, thanks to the static method, you can simply create a new instance of &lt;code&gt;Faker&amp;lt;Book&amp;gt;&lt;/code&gt;, ask it to generate one or more books, and enjoy the result:&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;Faker&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BogusBookGenerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateFaker&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;books&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Generate&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clearly, it's not necessary for the class to be marked as &lt;code&gt;static&lt;/code&gt;: it all depends on what you need to achieve!&lt;/p&gt;

&lt;h2&gt;
  
  
  Expose a subtype of Faker, specific to the data type to be generated
&lt;/h2&gt;

&lt;p&gt;If you don't want to use a method (static or not static, it doesn't matter), you can define a subtype of &lt;code&gt;Faker&amp;lt;Book&amp;gt;&lt;/code&gt; whose customization rules are all defined in the constructor.&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BookGenerator&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;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Book&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;public&lt;/span&gt; &lt;span class="nf"&gt;BookGenerator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&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;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&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="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&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;Text&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Genres&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EnumValues&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Genre&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;());&lt;/span&gt;
        &lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthorFirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthorLastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PagesCount&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;800&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nf"&gt;RuleForType&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;DateOnly&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PastDateOnly&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;Using this way, you can simply create a new instance of &lt;code&gt;BookGenerator&lt;/code&gt; and, again, call the &lt;code&gt;Generate&lt;/code&gt; method to create new book instances.&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;generator&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;BookGenerator&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;books&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Generate&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Method vs Subclass: When should we use which?
&lt;/h2&gt;

&lt;p&gt;As we saw, both methods bring the same result, and their usage is almost identical.&lt;/p&gt;

&lt;p&gt;So, which way should I use?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use the method&lt;/strong&gt; approach (the first one) when you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity&lt;/strong&gt;: If you need to generate fake data quickly and your rules are straightforward, using a method is the easiest approach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ad-hoc Data Generation&lt;/strong&gt;: Ideal for one-off or simple scenarios where you don't need to reuse the same rules across your application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or &lt;strong&gt;use the subclass&lt;/strong&gt; (the second approach) when you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reusability&lt;/strong&gt;: If you need to generate the same type of fake data in multiple places, defining a subclass allows you to encapsulate the rules and reuse them easily.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex scenarios and extensibility&lt;/strong&gt;: Better suited for more complex data generation scenarios where you might have many rules or need to extend the functionality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability&lt;/strong&gt;: Easier to maintain and update the rules in one place.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Further readings
&lt;/h2&gt;

&lt;p&gt;If you want to learn a bit more about Bogus and use it to populate data used by Entity Framework, I recently published an article about this topic:&lt;/p&gt;

&lt;p&gt;🔗&lt;a href="https://www.code4it.dev/blog/seed-inmemory-entityframework-bogus/" rel="noopener noreferrer"&gt;Seeding in-memory Entity Framework with realistic data with Bogus | Code4IT&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article first appeared on &lt;a href="https://www.code4it.dev/" rel="noopener noreferrer"&gt;Code4IT 🐧&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;However, the best place to learn about Bogus is the official documentation on GitHub.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://github.com/bchavez/Bogus/" rel="noopener noreferrer"&gt;Bogus repository | GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;This article sort of complements &lt;a href="https://www.code4it.dev/blog/seed-inmemory-entityframework-bogus/" rel="noopener noreferrer"&gt;the previous article about Bogus&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I think Bogus is one of the best libraries in the .NET universe, as having realistic data can help you improve the intelligibility of the test cases you generate. Also, Bogus can be a great tool when you want to showcase demo values without accessing real data.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>bogus</category>
      <category>testing</category>
    </item>
    <item>
      <title>Easy logging management with Seq and ILogger in ASP.NET</title>
      <dc:creator>Davide Bellone</dc:creator>
      <pubDate>Mon, 16 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/bellonedavide/easy-logging-management-with-seq-and-ilogger-in-aspnet-51ej</link>
      <guid>https://dev.to/bellonedavide/easy-logging-management-with-seq-and-ilogger-in-aspnet-51ej</guid>
      <description>&lt;p&gt;Logging is one of the most essential parts of any application.&lt;/p&gt;

&lt;p&gt;Wouldn't it be great if we could scaffold and use a logging platform with just a few lines of code?&lt;/p&gt;

&lt;p&gt;In this article, we are going to learn how to install and use Seq as a destination for our logs, and how to make an ASP.NET 8 API application send its logs to Seq by using the native logging implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seq: a sink and dashboard to manage your logs
&lt;/h2&gt;

&lt;p&gt;In the context of logging management, &lt;strong&gt;a "sink" is a receiver of the logs generated by one or many applications&lt;/strong&gt;; it can be a cloud-based system, but it's not mandatory: even a file on your local file system can be considered a sink.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://docs.datalust.co/v4/docs/getting-started" rel="noopener noreferrer"&gt;Seq&lt;/a&gt; is a Sink, and works by exposing a server&lt;/strong&gt; that stores logs and events generated by an application. Clearly, other than just storing the logs, Seq allows you to view them, access their details, perform queries over the collection of logs, and much more.&lt;/p&gt;

&lt;p&gt;It's free to use for individual usage, and comes with several pricing plans, depending on the usage and the size of the team.&lt;/p&gt;

&lt;p&gt;Let's start small and install the free version.&lt;/p&gt;

&lt;p&gt;We have two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download it locally, using an installer (&lt;a href="https://datalust.co/Download" rel="noopener noreferrer"&gt;here's the download page&lt;/a&gt;);&lt;/li&gt;
&lt;li&gt;Use Docker: pull the &lt;code&gt;datalust/seq&lt;/code&gt; image locally and run the container on your Docker engine.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both ways will give you the same result.&lt;/p&gt;

&lt;p&gt;However, if you already have experience with Docker, I suggest you use the second approach.&lt;/p&gt;

&lt;p&gt;Once you have Docker installed and running locally, open a terminal.&lt;/p&gt;

&lt;p&gt;First, you have to &lt;strong&gt;pull the Seq image locally&lt;/strong&gt; (I know, it's not mandatory, but I prefer doing it in a separate step):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;datalust/seq&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, when you have it downloaded, you can start a new instance of Seq locally, exposing the UI on a specific port.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;seq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--restart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;unless-stopped&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ACCEPT_EULA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;5341:80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;datalust/seq:latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down the previous command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker run&lt;/code&gt;: This command is used to create and start a new Docker container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--name seq&lt;/code&gt;: This option assigns the name &lt;em&gt;seq&lt;/em&gt; to the container. Naming containers can make them easier to manage.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-d&lt;/code&gt;: This flag runs the container in &lt;em&gt;detached&lt;/em&gt; mode, meaning it runs in the background.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--restart unless-stopped&lt;/code&gt;: This option ensures that the container will always restart unless it is explicitly stopped. This is useful for ensuring that the container remains running even after a reboot or if it crashes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-e ACCEPT_EULA=Y&lt;/code&gt;: This sets an &lt;em&gt;environment variable&lt;/em&gt; inside the container. In this case, it sets &lt;code&gt;ACCEPT_EULA&lt;/code&gt; to &lt;code&gt;Y&lt;/code&gt;, which likely indicates that you accept the End User License Agreement (EULA) for the software running in the container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-p 5341:80&lt;/code&gt;: This maps port 5341 on your host machine to port 80 in the container. This allows you to access the service running on port 80 inside the container via port 5341 on your host.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;datalust/seq:latest&lt;/code&gt;: This specifies the Docker image to use for the container. &lt;code&gt;datalust/seq&lt;/code&gt; is the image name, and &lt;code&gt;latest&lt;/code&gt; is the tag, indicating that you want to use the latest version of this image.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, this command runs a container named &lt;code&gt;seq&lt;/code&gt; in the background, ensures it restarts unless stopped, sets an environment variable to accept the EULA, maps a host port to a container port, and uses the latest version of the &lt;code&gt;datalust/seq&lt;/code&gt; image.&lt;/p&gt;

&lt;p&gt;It's important to pay attention to the used port: &lt;strong&gt;by default, Seq uses port 5341 to interact with the UI and the API&lt;/strong&gt;. If you prefer to use another port, feel free to do that - just remember that you'll need some additional configuration.&lt;/p&gt;

&lt;p&gt;Now that Seq is installed on your machine, you can access its UI. Guess what? It's on &lt;code&gt;localhost:5341&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fpvfobh69mvyvqjsr8drj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fpvfobh69mvyvqjsr8drj.png" alt="Seq brand new instance" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, Seq is "just" a container for our logs - but we have to produce them.&lt;/p&gt;

&lt;h2&gt;
  
  
  A sample ASP.NET API project
&lt;/h2&gt;

&lt;p&gt;I've created a simple API project that exposes CRUD operations for a data model stored in memory (we don't really care about the details).&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="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&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;"[controller]"&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;BooksController&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;public&lt;/span&gt; &lt;span class="nf"&gt;BooksController&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;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&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;Book&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetBook&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;FromRoute&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;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;booksCatalogue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SingleOrDefault&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;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;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="k"&gt;switch&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;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&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;book&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;p&gt;As you can see, the details here are not important.&lt;/p&gt;

&lt;p&gt;Even the &lt;code&gt;Main&lt;/code&gt; method is the default one:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddEndpointsApiExplorer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsDevelopment&lt;/span&gt;&lt;span class="p"&gt;())&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;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="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;UseHttpsRedirection&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;MapControllers&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;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have the Controllers, we have Swagger... well, nothing fancy.&lt;/p&gt;

&lt;p&gt;Let's mix it all together.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to integrate Seq with an ASP.NET application
&lt;/h2&gt;

&lt;p&gt;If you want to use Seq in an ASP.NET application (may it be an API application or whatever else), you have to add it to the startup pipeline.&lt;/p&gt;

&lt;p&gt;First, you have to &lt;strong&gt;install the proper NuGet package&lt;/strong&gt;: &lt;code&gt;Seq.Extensions.Logging&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fef5hehd05i1mfg1nsqzf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fef5hehd05i1mfg1nsqzf.png" alt="The Seq.Extensions.Logging NuGet package" width="800" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, you have to add it to your &lt;code&gt;Services&lt;/code&gt;, calling the &lt;code&gt;AddSeq()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;var builder = WebApplication.CreateBuilder(args);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;builder.Services.AddControllers();
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ builder.Services.AddLogging(lb =&amp;gt; lb.AddSeq());
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;var app = builder.Build();
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, Seq is ready to intercept whatever kind of log arrives at the specified port (remember, in our case, we are using the default one: 5341).&lt;/p&gt;

&lt;p&gt;We can try it out by adding an &lt;code&gt;ILogger&lt;/code&gt; to the &lt;code&gt;BooksController&lt;/code&gt; constructor:&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;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BooksController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&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;BooksController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BooksController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&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;So that we can use the &lt;code&gt;_logger&lt;/code&gt; instance to create logs as we want, using the necessary &lt;strong&gt;Log Level&lt;/strong&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;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&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;Book&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetBook&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;FromRoute&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;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I am Information"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogWarning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I am Warning"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I am Error"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogCritical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I am Critical"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;booksCatalogue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SingleOrDefault&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;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;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="k"&gt;switch&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;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&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;book&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;&lt;a href="https://media2.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%2Ftwc4mmc0ukqh1tnzb2me.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ftwc4mmc0ukqh1tnzb2me.png" alt="Log messages on Seq" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Structured Logging with ILogger and Seq
&lt;/h2&gt;

&lt;p&gt;One of the best things about Seq is that it automatically handles &lt;strong&gt;Structured Logging&lt;/strong&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;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&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;Book&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetBook&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;FromRoute&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;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Looking if in my collection with {TotalBooksCount} books there is one with ID {SearchedId}"&lt;/span&gt;
 &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;booksCatalogue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&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;Book&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;booksCatalogue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SingleOrDefault&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;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;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="k"&gt;switch&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;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&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;book&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;Have a look at this line:&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;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Looking if in my collection with {TotalBooksCount} books there is one with ID {SearchedId}"&lt;/span&gt;
 &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;booksCatalogue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line generates a string message, &lt;strong&gt;replaces all the placeholders&lt;/strong&gt;, and, on top of that, creates two properties, &lt;code&gt;SearchedId&lt;/code&gt; and &lt;code&gt;TotalBooksCount&lt;/code&gt;; you can now define queries using these values.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ffh380t89zw901dih25zn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ffh380t89zw901dih25zn.png" alt="Structured Logs in Seq allow you to view additional logging properties" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Further readings
&lt;/h2&gt;

&lt;p&gt;I have to admit it: &lt;strong&gt;logging management is one of my favourite topics&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I've already written a sort of introduction to Seq in the past, but at that time, I did not use the native &lt;code&gt;ILogger&lt;/code&gt;, but Serilog, a well-known logging library that added some more functionalities on top of the native logger.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://www.code4it.dev/blog/logging-with-serilog-and-seq/" rel="noopener noreferrer"&gt;Logging with Serilog and Seq | Code4IT&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article first appeared on &lt;a href="https://www.code4it.dev/" rel="noopener noreferrer"&gt;Code4IT 🐧&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In particular, Serilog can be useful for propagating Correlation IDs across multiple services so that you can fetch all the logs generated by a specific operation, even though they belong to separate applications.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://www.code4it.dev/blog/serilog-correlation-id/" rel="noopener noreferrer"&gt;How to log Correlation IDs in .NET APIs with Serilog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to search through my blog for all the articles related to logging - I'm sure you will find interesting stuff!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;I think Seq is the best tool for local development&lt;/strong&gt;: it's easy to download and install, supports structured logging, and can be easily added to an ASP.NET application with just a line of code.&lt;/p&gt;

&lt;p&gt;I usually add it to my private projects, especially when the operations I run are complex enough to require some well-structured log.&lt;/p&gt;

&lt;p&gt;Given how it's easy to install, sometimes I use it for my work projects too: when I have to fix a bug, but I don't want to use the centralised logging platform (since it's quite complex to use), I add Seq as a destination sink, run the application, and analyse the logs in my local machine. Then, of course, I remove its reference, as I want it to be just a discardable piece of configuration.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>logging</category>
      <category>seq</category>
      <category>ilogger</category>
    </item>
    <item>
      <title>Introducing the Testing Vial: a (better?) alternative to Testing Diamond and Testing Pyramid</title>
      <dc:creator>Davide Bellone</dc:creator>
      <pubDate>Mon, 03 Nov 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/bellonedavide/introducing-the-testing-vial-a-better-alternative-to-testing-diamond-and-testing-pyramid-3cfk</link>
      <guid>https://dev.to/bellonedavide/introducing-the-testing-vial-a-better-alternative-to-testing-diamond-and-testing-pyramid-3cfk</guid>
      <description>&lt;p&gt;Testing is crucial in any kind of application. It can also be important in applications that are meant to be thrown away: in fact, with a proper testing strategy, you can ensure that the application will do exactly what you expect it to do; instead of running it over and over again to fix the parts, by adding some specific tests, you will speed up the development of that throwaway project.&lt;/p&gt;

&lt;p&gt;The most common testing strategies are the &lt;em&gt;Testing Pyramid&lt;/em&gt; and the &lt;em&gt;Testing Diamond&lt;/em&gt;. &lt;strong&gt;They are both useful, but I think that they are not perfect&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's why I came up with a new testing strategy that I called "&lt;strong&gt;the Testing Vial&lt;/strong&gt;": in this article, I'm going to introduce it and explain the general idea. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Since it's a new idea, I'd like to hear your honest feedback. Don't be afraid to tell me that this is a terrible idea - let's work on it together!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Testing Pyramid: the focus is on Unit Tests
&lt;/h2&gt;

&lt;p&gt;The Testing Pyramid is a testing strategy where &lt;strong&gt;the focus is on Unit Tests&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Unit Tests are easy to write (well, they are &lt;em&gt;often&lt;/em&gt; easy to write: it depends on whether your codebase is a mess!), they are fast to execute, so they provide immediate feedback.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F5duv7f6jmt5qgcnd5ebd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F5duv7f6jmt5qgcnd5ebd.png" alt="The testing pyramid" width="509" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, the focus here is on technical details: if you create a class named &lt;code&gt;Foo&lt;/code&gt;, most probably you will have its sibling class &lt;code&gt;FooTests&lt;/code&gt;. And the same goes for each (public) method in it.&lt;/p&gt;

&lt;p&gt;Yes, I know: &lt;strong&gt;unit tests can operate across several methods of the same class, as long as it is considered a "unit"&lt;/strong&gt;. But let's be real: most of the time, we write tests against each single public method. And, even worse, we are overusing mocks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problems with the Testing Pyramid
&lt;/h3&gt;

&lt;p&gt;The Testing Pyramid relies too much on unit tests. &lt;/p&gt;

&lt;p&gt;But Unit Tests are not perfect:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They often &lt;strong&gt;rely too much on mocks&lt;/strong&gt;: tests might not reflect the real execution of the system;&lt;/li&gt;
&lt;li&gt;They are &lt;strong&gt;too closely coupled with the related class and method&lt;/strong&gt;: if you add one parameter to one single method, you most probably will have to update tens of test methods;&lt;/li&gt;
&lt;li&gt;They &lt;strong&gt;do not reflect the business operations&lt;/strong&gt;: you might end up creating the strongest code ever, but missing the point of the whole business meaning. Maybe, because you focused too much on technical details and forgot to evaluate all the acceptance criteria.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, suppose that you have to change something big, like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add OpenTelemetry support on the whole system;&lt;/li&gt;
&lt;li&gt;replace SQL with MongoDB;&lt;/li&gt;
&lt;li&gt;refactor a component, replacing a huge internal switch-case block with the Chain Of Responsibility pattern.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, in this case, you will have to update or delete a lot of Unit Tests. And, still, you might not be sure you haven't added regressions. This is one of the consequences of focusing too much on Unit Tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Testing Diamond: the focus is on Integration Tests
&lt;/h2&gt;

&lt;p&gt;The Testing Diamond emphasises the &lt;strong&gt;importance of Integration Tests&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fd1n0gvvy91c52mm2lccv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fd1n0gvvy91c52mm2lccv.png" alt="The Testing Diamond" width="480" height="624"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, when using this testing strategy, you are expected to write many more Integration Tests and way fewer Unit Tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In my opinion, this is a better approach to testing&lt;/strong&gt;: this way, you can focus more on the business value and less on the technical details.&lt;/p&gt;

&lt;p&gt;Using this approach, you may refactor huge parts of the system without worrying too much about regressions and huge changes in tests: in fact, &lt;strong&gt;Integration Tests will give you a sort of safety net&lt;/strong&gt;, ensuring that the system still works as expected.&lt;/p&gt;

&lt;p&gt;So, if I had to choose, I'd go with the Testing Diamond: implementations may change, while the overall application functionality will still be preserved. &lt;/p&gt;

&lt;h3&gt;
  
  
  Problems with the Testing Diamond
&lt;/h3&gt;

&lt;p&gt;Depending on the size of the application and on how it is structured, Integration Tests may be time-consuming and hard to spin up.&lt;/p&gt;

&lt;p&gt;Maybe you have a gigantic monolith that takes minutes to start up: in this case, running Integration Tests may take literally hours.&lt;/p&gt;

&lt;p&gt;Also, there is &lt;strong&gt;a problem with data&lt;/strong&gt;: if you are going to write data to a database (or an external resource), how can you ensure that the operation does not insert duplicate or dirty data?&lt;/p&gt;

&lt;p&gt;For this problem, there are several solutions, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;using Ephemeral Environments specifically to run these tests;&lt;/li&gt;
&lt;li&gt;using TestContainers to create a sandbox environment;&lt;/li&gt;
&lt;li&gt;replacing some specific operations (like saving data on the DB or sending HTTP requests) by using a separate, standalone service (as we learned &lt;a href="https://www.code4it.dev/blog/advanced-integration-tests-webapplicationfactory/#how-to-set-up-custom-dependencies-for-your-tests" rel="noopener noreferrer"&gt;in this article&lt;/a&gt;, where we customised a WebApplicationFactory).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those approaches may not be easy to implement, I know.&lt;/p&gt;

&lt;p&gt;Also, &lt;strong&gt;Integration Tests alone may not cover all the edge cases&lt;/strong&gt;, making your application less robust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the Testing Vial: the focus is on business entities
&lt;/h2&gt;

&lt;p&gt;Did you notice? Both the Testing Pyramid and the Testing Diamond focus on the technical aspects of the tests, and not on the meaning for the business. &lt;/p&gt;

&lt;p&gt;I think that is a wrong approach, and that we should really &lt;strong&gt;shift our focus from the number of tests of a specific type&lt;/strong&gt; (more Unit Tests or more Integration Tests?) &lt;strong&gt;to the organisational value they bring&lt;/strong&gt;: that's why I came up with the idea of the Testing Vial.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fe7bicg5crtx2b9h93911.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fe7bicg5crtx2b9h93911.png" alt="The Testing Vial" width="480" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can imagine tests to be organised into &lt;strong&gt;sealed&lt;/strong&gt; vials.&lt;/p&gt;

&lt;p&gt;In each vial, you have&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;E2E tests: to at least cover &lt;strong&gt;the most critical flows&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Integration tests: to cover at least &lt;strong&gt;all the business requirements&lt;/strong&gt; as they are described in the Acceptance Criteria of your User Stories (or, in general, to cover all Happy Paths and the most common Unhappy Paths);&lt;/li&gt;
&lt;li&gt;Unit test: to cover at least &lt;strong&gt;all the edge cases&lt;/strong&gt; that are hard to reproduce with Integration tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, using the Testing Vial, you don't have to worry about the number of tests of a specific type: you only care that, regardless of their number, tests are focused on Business concerns.&lt;/p&gt;

&lt;p&gt;But, ok, nothing fancy: it's just common sense.&lt;/p&gt;

&lt;p&gt;To make the Testing Vial effective, there are two more parts to add.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architectural tests, to validate that the system design hasn't changed
&lt;/h3&gt;

&lt;p&gt;After you have all these tests, in a variable number which depends solely on what is actually helpful for you, you also write some Architectural Tests, for example, by using &lt;a href="https://www.archunit.org/" rel="noopener noreferrer"&gt;ArchUnit&lt;/a&gt;, for Java, or &lt;a href="https://github.com/TNG/ArchUnitNET" rel="noopener noreferrer"&gt;ArchUnit.NET&lt;/a&gt; for .NET applications.&lt;/p&gt;

&lt;p&gt;This way, other than focusing on the business value (regardless of this goal being achieved by Unit Tests or Integration Tests), you also &lt;strong&gt;validate that the system hasn't changed in unexpected ways&lt;/strong&gt;. For example, you might have added a dependency between modules, making the system more coupled and less maintainable. &lt;/p&gt;

&lt;p&gt;Generally speaking, Architectural Tests should be written in the initial phases of a project, so that, by running them from time to time, they can ensure that nothing has changed.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;Architectural Tests, which act as a cap for the vial&lt;/strong&gt;, you ensure that the tests are complete, valid, and that the architecture-wise maintainability of the system is preserved.&lt;/p&gt;

&lt;p&gt;But that's not enough!&lt;/p&gt;

&lt;h3&gt;
  
  
  Categories, to identify and isolate areas of your application
&lt;/h3&gt;

&lt;p&gt;All of this makes sense if you add &lt;strong&gt;one or more tags&lt;/strong&gt; to your tests: these tags should identify the business entity the test is referring to. For example, in an e-shop application, you should add categories about "Product", "Cart", "User", and so on. This is way easier if you already do DDD, clearly.&lt;/p&gt;

&lt;p&gt;In C# you can categorise tests by using &lt;code&gt;TestCategory&lt;/code&gt; if you use MSTest or NUnit, or &lt;code&gt;Trait&lt;/code&gt; if you use xUnit.*&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;TestCategory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cart"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;TestCategory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User"&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;User_Should_DoSomethingWithCart&lt;/span&gt;&lt;span class="p"&gt;(){}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, but why?&lt;/p&gt;

&lt;p&gt;Well, categorising tests allows you to &lt;strong&gt;keep track of the impacts of a change more broadly&lt;/strong&gt;. Especially at the beginning, you might notice that too many tests are marked with too many categories: this might be a sign of a poor design, and you might want to work to improve it.&lt;/p&gt;

&lt;p&gt;Also, by grouping by category, you can &lt;strong&gt;have a complete view of everything that happens in the system&lt;/strong&gt; about that specific Entity, regardless of the type of test.&lt;/p&gt;

&lt;p&gt;Did you know that in Visual Studio you can group tests by Category (called &lt;strong&gt;Traits&lt;/strong&gt;), so that you can see and execute all the tests related to a specific Category?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F6c8y5optkx91mzjhibez.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F6c8y5optkx91mzjhibez.png" alt="Tests grouped by Category in Visual Studio 2022" width="669" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By using Code Coverage tools wisely - executing them in combination with tests of a specific category - you can identify all the parts of the application that are affected by such tests. This is especially true if you have many Integration Tests: just by looking at the executed methods, you can have a glimpse of all the parts touched by that test. This simple trick can also help you out with reorganising the application (maybe by moving from monolith to modular monolith).&lt;/p&gt;

&lt;p&gt;Finally, having tests tagged, allows you to &lt;strong&gt;have a catalogue of all the Entities and their dependencies&lt;/strong&gt;. And, in case you need to work on a specific activity that changes something about an Entity, you can perform better analyses to find potential, overlooked impacts.&lt;/p&gt;

&lt;p&gt;And each vial can have a different size, but they will all be stored on a "shelf" that lets you have a glimpse at the whole system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fuf3k9r4epad24xza5zdq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fuf3k9r4epad24xza5zdq.png" alt="Vials can be stored on a shelf" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Further readings
&lt;/h2&gt;

&lt;p&gt;There is a lot of content about tests and testing strategies, so here are some of them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.testim.io/blog/end-to-end-testing-vs-integration-testing/" rel="noopener noreferrer"&gt;End-to-End Testing vs Integration Testing | Testim&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article first appeared on &lt;a href="https://www.code4it.dev/" rel="noopener noreferrer"&gt;Code4IT 🐧&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this article I described how I prefer the Testing Diamond over the Testing Pyramid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.code4it.dev/architecture-notes/testing-pyramid-vs-testing-diamond/" rel="noopener noreferrer"&gt;Testing Pyramid vs Testing Diamond (and how they affect Code Coverage) | Code4IT&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Then, I clearly changed my mind and came up with the idea of the Testing Vial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;With the Testing Vial approach, &lt;strong&gt;the shift moves from technical to business concerns&lt;/strong&gt;: you don't really care if you've written more Unit Tests or more Integration tests; you only care that you have covered everything that the business requires, and that by using Architecture Tests and Test Categories you can make sure that you are not introducing unwanted dependencies between modules, improving maintainability. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vials are meant to be standalone&lt;/strong&gt;: by accessing the content of a vial, you can see everything related to it: its dependencies, its architecture, main user cases and edge cases.&lt;/p&gt;

&lt;p&gt;Clearly, the same test may appear in multiple vials, but that's not a problem.&lt;/p&gt;

&lt;p&gt;I came up with this idea recently, so I want to hear from you what you think about it. I'm sure there are areas of improvement!&lt;/p&gt;

&lt;p&gt;Let me know!&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this article! Let's keep in touch on &lt;a href="https://www.linkedin.com/in/BelloneDavide/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://twitter.com/BelloneDavide" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://bsky.app/profile/bellonedavide.bsky.social" rel="noopener noreferrer"&gt;BlueSky&lt;/a&gt;! 🤜🤛&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;🐧&lt;/p&gt;

</description>
      <category>testing</category>
      <category>testingpyramid</category>
      <category>testingdiamond</category>
      <category>testingvial</category>
    </item>
    <item>
      <title>Davide's Code and Architecture Notes - the Error Management Trio, and how it affects your software architecture</title>
      <dc:creator>Davide Bellone</dc:creator>
      <pubDate>Tue, 05 Nov 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/bellonedavide/davides-code-and-architecture-notes-the-error-management-trio-and-how-it-affects-your-software-1hhd</link>
      <guid>https://dev.to/bellonedavide/davides-code-and-architecture-notes-the-error-management-trio-and-how-it-affects-your-software-1hhd</guid>
      <description>&lt;p&gt;When designing a new software system, it's easy to focus mainly on the happy flow and forget that you must also handle errors.&lt;/p&gt;

&lt;p&gt;You should carefully define and design how to handle errors: &lt;strong&gt;depending on the use case, error handling can have a huge impact on the architecture of your software system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this article, we'll explore the three main categories of errors that we must always remember to address; for each type of error, we will showcase how addressing it can impact the software architecture differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  An ideal system with only the happy path
&lt;/h2&gt;

&lt;p&gt;To use a realistic example, let's design a simple system with a single module named &lt;em&gt;MainApplication&lt;/em&gt;: this module reads data from an external API, manipulates the data, and stores the result on the DB.&lt;/p&gt;

&lt;p&gt;The system is called asynchronously, via a Message Queue, by an external service - that we are going to ignore.&lt;/p&gt;

&lt;p&gt;The happy flow is pretty much the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An external system inserts some data into the Queue;&lt;/li&gt;
&lt;li&gt;MainApplication reads the data from the Queue;&lt;/li&gt;
&lt;li&gt;MainApplication calls an external API to retrieve some data;&lt;/li&gt;
&lt;li&gt;MainApplication stores some data on the DB;&lt;/li&gt;
&lt;li&gt;MainApplication sends a message on the queue with the operation result.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fy12752y82yghm3dqfxnu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fy12752y82yghm3dqfxnu.png" alt="Happy flow for MainApplication" width="501" height="148"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, the happy flow is simple. But we should have covered what to do in case of an error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the Error Management Trio
&lt;/h2&gt;

&lt;p&gt;In general, errors that need to be handled fall into three categories (that I decided to call "the Error Management Trio"): data validation, transient errors, and faults.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data Validation&lt;/strong&gt; focuses on the data used across the system, particularly the &lt;strong&gt;data you don't control&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transient Errors&lt;/strong&gt; occur when the application's overall status or its dependencies &lt;strong&gt;temporarily&lt;/strong&gt; change to an invalid state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Faults&lt;/strong&gt; are errors that take down the whole application, and you cannot recover immediately.&lt;/p&gt;

&lt;p&gt;The Trio does not take into account "errors" that are not properly errors: null values, queries that do not return any value, and so on. These, in my opinion, are all legitimate statuses that represent that lack of values but are not errors that have architectural relevance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fwjxa24x9kcdd7rd3zzq2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fwjxa24x9kcdd7rd3zzq2.png" alt="The Error Management Trio schema" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Validation: the first defence against invalid status
&lt;/h2&gt;

&lt;p&gt;The Data Validation category focuses on &lt;strong&gt;ensuring that relevant data is in a valid status&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In particular, it aims to ensure that data coming from external sources (for example, from the Body of an incoming HTTP request or from the result of a database query) is both syntactically and logically valid.&lt;/p&gt;

&lt;p&gt;Suppose that the messages we receive from the queue are in the following format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mr. captain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"BookId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;154&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Add"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We definitely need to perform some sort of validation on the message content.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Username&lt;/code&gt; property must not be empty;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;BookId&lt;/code&gt; property must be a positive number;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Operation&lt;/code&gt; property must have one of the following values: &lt;em&gt;Add&lt;/em&gt;, &lt;em&gt;Remove&lt;/em&gt;, &lt;em&gt;Refresh&lt;/em&gt;;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How does it impact our design?&lt;/p&gt;

&lt;p&gt;We have several choices to deal with an invalid incoming message:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ignore the whole message: if it doesn't pass the validation, discard the message;&lt;/li&gt;
&lt;li&gt;send the message back to the caller, describing the type of error&lt;/li&gt;
&lt;li&gt;try to fix it locally: if we are able to recreate a valid message, we could try to fix it and process the incoming message;&lt;/li&gt;
&lt;li&gt;try to fix it in a separate service: you will need to create a distinct service that receives the invalid message and tries to fix it: if it manages to fix the message, it re-inserts it in the original queue; otherwise, it sends a message to the response queue to notify about the impossibility of recreating a valid message.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As you can see, even for the simple input validation, the choices we make can have an impact on the structure of the architecture.&lt;/p&gt;

&lt;p&gt;Suppose that you choose option #4: you will need to implement a brand new service (let's call it &lt;em&gt;ValidationFixesManager&lt;/em&gt;), configure a new queue, and keep track of the attempts to fix the message.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F5fxnbc8y2u1m9pq4ayqn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F5fxnbc8y2u1m9pq4ayqn.png" alt="Example of Architecture with ValidationFixesManager component" width="501" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of this only when considering the static validation. &lt;strong&gt;How would you validate your business rules?&lt;/strong&gt; How would you ensure that, for instance, the Username is valid and the user is still active on the system?&lt;/p&gt;

&lt;p&gt;Maybe you discover that the data stored on the database in incomplete or stale. Then you have to work out a way to handle such type of data.&lt;/p&gt;

&lt;p&gt;For example, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run a background job that ensures that all the data is always valid;&lt;/li&gt;
&lt;li&gt;enrich the data from the DB with newer data only when it is actually needed;&lt;/li&gt;
&lt;li&gt;fine-tune the database consistency level.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have just demonstrated a simple but important fact: &lt;strong&gt;data validation &lt;em&gt;looks&lt;/em&gt; trivial&lt;/strong&gt;, but depending on the needs of your system, it may impact how you design your system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transient Errors: temporary errors that may randomly occur
&lt;/h2&gt;

&lt;p&gt;Even if the validation passes, &lt;strong&gt;temporary issues&lt;/strong&gt; may prevent your operations from completing.&lt;/p&gt;

&lt;p&gt;In the previous example, there are some possible cases to consider:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the external API is temporarily down, and you cannot retrieve the data you need;&lt;/li&gt;
&lt;li&gt;the return queue is full, and you cannot add response messages;&lt;/li&gt;
&lt;li&gt;the application is not able to connect to the DB due to network issues;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These kinds of issues are due to a temporary status of the system or of one of its dependencies.&lt;/p&gt;

&lt;p&gt;Sure, you may &lt;strong&gt;add automatic retries&lt;/strong&gt;: for instance, you can use Polly to automatically retry accessing the API. But what if it's not enough?&lt;/p&gt;

&lt;p&gt;Again, depending on your application's requirements and the overall structure you started designing, solving this problem may bring you to unexpected paths.&lt;/p&gt;

&lt;p&gt;Let's say that the external API is returning a 500 HTTP error: this is a transient error, and it does not depend on the content of the request: the API is down, and you cannot do anything to solve it.&lt;/p&gt;

&lt;p&gt;What can we do if all the retries fail?&lt;/p&gt;

&lt;p&gt;If we can just accept the situation, we can return the error to the caller and move on with the next operation.&lt;/p&gt;

&lt;p&gt;But if we need to keep trying until the operation goes well, we have (at least) two choices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;consume the message from the Queue, try calling the API, and, if it fails, re-insert the message on the queue (ideally, with some delay);&lt;/li&gt;
&lt;li&gt;peek the message from the queue and try calling the API. If it fails, the message stays on the queue (and you need a way to read it again). Otherwise, we consider the message completed and remove it from the queue.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These are just two of the different solutions. But, as you can see, this choice will have, in the long run, a huge effect on the future of the application, both in terms of maintainability and performance.&lt;/p&gt;

&lt;p&gt;Below is how the structure changes if we decide to reinsert the failed messages into the queue with a delay.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fuppbvvwaxzet04nq0uoo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fuppbvvwaxzet04nq0uoo.png" alt="The MainApplication now sends messages back on the queue" width="501" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In both cases, we must remember that trying to call a service that is temporarily down is useless: maybe it's time to &lt;strong&gt;use a Circuit Breaker&lt;/strong&gt;?&lt;/p&gt;

&lt;h2&gt;
  
  
  Fatal Errors: when everything goes wrong
&lt;/h2&gt;

&lt;p&gt;There is one type of error that is often neglected but that may deeply influence how your system behaves: fatal errors.&lt;/p&gt;

&lt;p&gt;Examples of fatal errors are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the host has consumed all the CPU or RAM;&lt;/li&gt;
&lt;li&gt;the file system is corrupted;&lt;/li&gt;
&lt;li&gt;the connection to an external system is interrupted due to network misconfigurations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, fatal errors are errors you have no way to solve in the short run: they happen and stop everything you are doing.&lt;/p&gt;

&lt;p&gt;This kind of error cannot be directly managed via application code, but you need to rely on other techniques.&lt;/p&gt;

&lt;p&gt;For example, to make sure you won't consume all the available RAM, you should plan for autoscaling of your resources. So you have to design the system with autoscaling in mind: this means, for example, that the system must be stateless and the application must run on infrastructure objects that can be configured to automatically manage resources (like Azure Functions, Kubernetes, and Azure App Services). Also: do you need horizontal or vertical scaling?&lt;/p&gt;

&lt;p&gt;And, talking about &lt;strong&gt;the integrity of the system&lt;/strong&gt;, how do you ensure that operations that were ongoing when the fatal error occurred can be completed?&lt;/p&gt;

&lt;p&gt;One possible solution is to use a database table to keep track of the status of each operation, so that when the application restarts, it first completes pending operations, and then starts working on new operations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/.%2Fwith-db-for-tracking-status.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/.%2Fwith-db-for-tracking-status.png" alt="A database keeps track of the failed operations" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical approach to address the Error Management Trio
&lt;/h2&gt;

&lt;p&gt;There are too many errors to manage and too much effort to cover everything!&lt;/p&gt;

&lt;p&gt;How can we cover everything? Well, it's impossible: &lt;strong&gt;for every action we take to prevent an error, a new one may occur&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let's jump back to the example we saw for handling validation errors (using a new service that tries to fix the message). What if the &lt;em&gt;ValidationFixesManager&lt;/em&gt; service is down or the message queue is unreachable? We tried to solve a problem, but we ended up with two more to be managed!&lt;/p&gt;

&lt;p&gt;Let me introduce a practical approach to help you decide what needs to be addressed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: list all the errors you can think of&lt;/strong&gt;. Create a table to list all the possible errors that you expect to happen.&lt;/p&gt;

&lt;p&gt;You can add a column to describe the category the error falls into, as well as a Probability and Impact on the system column with a value (in this example, Low, Medium and High) that represents the probability that this error occurs and the impact it has on the overall application.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Probability&lt;/th&gt;
&lt;th&gt;Impact on the system&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Invalid message from queue&lt;/td&gt;
&lt;td&gt;Data Validation&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Invalid user data on DB&lt;/td&gt;
&lt;td&gt;Data Validation&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Missing user on DB&lt;/td&gt;
&lt;td&gt;Data Validation&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API not reachable&lt;/td&gt;
&lt;td&gt;Transient&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB not reachable&lt;/td&gt;
&lt;td&gt;Transient&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File system corrupted&lt;/td&gt;
&lt;td&gt;Fatal&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU limit reached&lt;/td&gt;
&lt;td&gt;Fatal&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;From here, you can pick the most urgent elements to be addressed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: evaluate alternatives&lt;/strong&gt;. Every error can be addressed in several ways (&lt;strong&gt;ignoring the error IS a valid alternative!&lt;/strong&gt;). Take some time to explore all the alternatives.&lt;/p&gt;

&lt;p&gt;Again, a table can be a good companion for this step. You can describe, for example:&lt;br&gt;
the effort required to solve the error (Low, Medium, High)&lt;br&gt;
the positive and negative consequences in terms (also) of quality attributes (aka: "-ilities"). Maybe a solution works fine for data integrity but has a negative impact on maintainability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: use ADRs to describe how (and why) you will handle that specific error&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Take your time to thoroughly describe, using ADR documents, the problems you are trying to solve, the solutions taken into consideration, and the final choice.&lt;/p&gt;

&lt;p&gt;Having everything written down in a shared file is fundamental for ensuring that, in the future, the present choices and necessities are taken into account, before saying "meh, that's garbage!"&lt;/p&gt;

&lt;h2&gt;
  
  
  Further readings
&lt;/h2&gt;

&lt;p&gt;Unfortunately, I feel that error handling is one of the most overlooked topics when designing a system. This also means that there are not lots and lots of articles and resources that explore this topic.&lt;/p&gt;

&lt;p&gt;But, if you use queues, one of the components you should use to manage errors is the Dead Letter queue. Here's a good article by Dorin Baba where he explains how to use Dead Letter queues to handle errors in asynchronous systems.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://medium.com/@dorinbaba/handling-errors-like-a-pro-or-nah-lets-talk-about-dead-letters-19a9a67869c1" rel="noopener noreferrer"&gt;Handling errors like a pro or nah? Let's talk about Dead Letters | Dorin Baba&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article first appeared on &lt;a href="https://www.code4it.dev/" rel="noopener noreferrer"&gt;Code4IT 🐧&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this article, we used a Queue to trigger the beginning of the operation. When using Azure services, we have two types of message queues: Queues and Topics. Do you know the difference? Hint: other vendors use the same names to represent different concepts.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://www.code4it.dev/blog/azure-service-bus-queue-vs-topic/" rel="noopener noreferrer"&gt;Azure Service Bus: Queues vs Topics | Code4IT&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whichever way you choose to solve and manage an error, always remember to write down the reasons that guided you to use that specific solution. An incredibly helpful way is by using ADRs.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://www.code4it.dev/architecture-notes/architecture-decision-records/" rel="noopener noreferrer"&gt;Tracking decision with Architecture Decision Records (ADRs) | CodeIT&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;This article highlights the importance of error management and the fact that even if we all want to avoid and prevent errors in our systems, we still have to take care of them and plan according to our needs.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>errors</category>
    </item>
    <item>
      <title>C# Tip: IEnumerable vs ICollection, and why it matters</title>
      <dc:creator>Davide Bellone</dc:creator>
      <pubDate>Tue, 15 Oct 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/bellonedavide/c-tip-ienumerable-vs-icollection-and-why-it-matters-5ac5</link>
      <guid>https://dev.to/bellonedavide/c-tip-ienumerable-vs-icollection-and-why-it-matters-5ac5</guid>
      <description>&lt;p&gt;Defining the best return type is crucial to creating a shared library whose behaviour is totally under your control.&lt;/p&gt;

&lt;p&gt;You should &lt;strong&gt;give the consumers of your libraries just the right amount of freedom&lt;/strong&gt; to integrate and use the classes and structures you have defined.&lt;/p&gt;

&lt;p&gt;That's why it is important to know the differences between interfaces like &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code&gt;ICollection&amp;lt;T&amp;gt;&lt;/code&gt;: these interfaces are often used together but have totally different meanings.&lt;/p&gt;

&lt;h2&gt;
  
  
  IEnumerable: loop through the items in the collection
&lt;/h2&gt;

&lt;p&gt;Suppose that &lt;code&gt;IAmazingInterface&lt;/code&gt; is an interface you expose so that clients can interact with it without knowing the internal behaviour.&lt;/p&gt;

&lt;p&gt;You have defined it this way:&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;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IAmazingInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IEnumerable&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;GetNumbers&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;numbers&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;As you can see, the &lt;code&gt;GetNumbers&lt;/code&gt; returns an &lt;code&gt;IEnumerable&amp;lt;int&amp;gt;&lt;/code&gt;: this means that (&lt;em&gt;unless they do some particular tricks like using reflection&lt;/em&gt;), clients will only be able to loop through the collection of items.&lt;/p&gt;

&lt;p&gt;Clients don't know that, behind the scenes, &lt;code&gt;AmazingClass&lt;/code&gt; uses a custom class &lt;code&gt;MySpecificEnumberable&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AmazingClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IAmazingInterface&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;IEnumerable&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;GetNumbers&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;numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MySpecificEnumberable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&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;code&gt;MySpecificEnumberable&lt;/code&gt; is a custom class whose purpose is to store the initial values in a sorted way. It implements &lt;code&gt;IEnumerable&amp;lt;int&amp;gt;&lt;/code&gt;, so the only operations you have to support are the two implementations of &lt;code&gt;GetEnumerator()&lt;/code&gt; - pay attention to the returned data type!&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MySpecificEnumberable&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&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="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="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;_numbers&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;MySpecificEnumberable&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;numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_numbers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&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="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumerator&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;GetEnumerator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;foreach&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;number&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;number&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;IEnumerator&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnumerator&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;_numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnumerator&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;Clients will then be able to loop all the items in the collection:&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;IAmazingInterface&lt;/span&gt; &lt;span class="n"&gt;something&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;AmazingClass&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;numbers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetNumbers&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="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="k"&gt;foreach&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;number&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&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;But you cannot add or remove items from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  ICollection: list, add, and remove items
&lt;/h2&gt;

&lt;p&gt;As we saw, &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; only allows you to loop through all the elements. However, you cannot add or remove items from an &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To do so, you need something that implements &lt;code&gt;ICollection&amp;lt;T&amp;gt;&lt;/code&gt;, like the following class (&lt;em&gt;I haven't implemented any of these methods: I want you to focus on the operations provided, not on the implementation details&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MySpecificCollection&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ICollection&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="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="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;NotImplementedException&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;bool&lt;/span&gt; &lt;span class="n"&gt;IsReadOnly&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;NotImplementedException&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;Add&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;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;NotImplementedException&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;Clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;NotImplementedException&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;bool&lt;/span&gt; &lt;span class="nf"&gt;Contains&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;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;NotImplementedException&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;CopyTo&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;array&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;arrayIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;NotImplementedException&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;IEnumerator&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;GetEnumerator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;NotImplementedException&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;bool&lt;/span&gt; &lt;span class="nf"&gt;Remove&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;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;NotImplementedException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;IEnumerator&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnumerator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;NotImplementedException&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;code&gt;ICollection&amp;lt;T&amp;gt;&lt;/code&gt; is a subtype of &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;, so everything we said before is still valid.&lt;/p&gt;

&lt;p&gt;However, having a class that implements &lt;code&gt;ICollection&amp;lt;T&amp;gt;&lt;/code&gt; gives you full control over how items can be added or removed from the collection, allowing you to define custom behaviour. For instance, you can define that the &lt;code&gt;Add&lt;/code&gt; method adds an integer only if it's an odd number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why knowing the difference actually matters
&lt;/h2&gt;

&lt;p&gt;Classes and interfaces are meant to be used. If you are like me, you work on both the creation of the class and its consumption.&lt;/p&gt;

&lt;p&gt;So, if an interface must return a sequence of items, you most probably use the &lt;code&gt;List&lt;/code&gt; shortcut: define the return type of the method as &lt;code&gt;List&amp;lt;Item&amp;gt;&lt;/code&gt;, and then use it, regardless of having it looped through or having the consumer add items to the sequence.&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="c1"&gt;// in the interface&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ISomething&lt;/span&gt;
&lt;span class="p"&gt;{&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;Item&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;PerformSomething&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;numbers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;// in the consumer class&lt;/span&gt;
&lt;span class="n"&gt;ISomething&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;//omitted&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;Item&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;myItems&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="nf"&gt;PerformSomething&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="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything works fine, but it works because we are in control of both the definition and the consumer.&lt;/p&gt;

&lt;p&gt;What if you have to expose the library to something outside your control?&lt;/p&gt;

&lt;p&gt;You have to consider two elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;consumers should not be able to &lt;strong&gt;tamper with your internal implementation&lt;/strong&gt; (for example, by adding items when they are not supposed to);&lt;/li&gt;
&lt;li&gt;you should be able to &lt;strong&gt;change the internal implementation&lt;/strong&gt; as you wish without breaking changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, if you want your users to just enumerate the items within a collection, you may start this way:&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="c1"&gt;// in the interface&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ISomething&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;PerformSomething&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;numbers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// in the implementation&lt;/span&gt;

&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;PerformSomething&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;numbers&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;numbers&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="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Item&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="c1"&gt;// in the consumer class&lt;/span&gt;

&lt;span class="n"&gt;ISomething&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;//omitted&lt;/span&gt;
&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;myItems&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="nf"&gt;PerformSomething&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="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, when the time comes, you can change the internal implementation of &lt;code&gt;PerformSomething&lt;/code&gt; with a more custom class:&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="c1"&gt;// custom IEnumerable definition&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;MyCustomEnumberable&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*omitted*/&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// in the interface&lt;/span&gt;
&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;PerformSomething&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;numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;MyCustomEnumberable&lt;/span&gt; &lt;span class="n"&gt;customEnumerable&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;MyCustomEnumberable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;customEnumerable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DoSomething&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&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;customEnumerable&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;And the consumer will not notice the difference. Again, unless they try to use tricks to tamper with your code!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article first appeared on &lt;a href="https://www.code4it.dev/" rel="noopener noreferrer"&gt;Code4IT 🐧&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;While understanding the differences between &lt;code&gt;IEnumerable&lt;/code&gt; and &lt;code&gt;ICollection&lt;/code&gt; is trivial, understanding why you should care about them is not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fczmn96pyquyfpm0fi3ai.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fczmn96pyquyfpm0fi3ai.png" alt="IEnumerable and ICollection hierarchy" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this article helped you understand that yeah, you can take the easy way and return everywhere a &lt;code&gt;List&lt;/code&gt;, but it's a choice that you cannot always apply to a project, and that probably will make breaking changes more frequent in the long run.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>ienumerable</category>
      <category>icollection</category>
    </item>
    <item>
      <title>How to use IHttpClientFactory and WireMock.NET together using Moq</title>
      <dc:creator>Davide Bellone</dc:creator>
      <pubDate>Tue, 01 Oct 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/bellonedavide/how-to-use-ihttpclientfactory-and-wiremocknet-together-using-moq-2kl0</link>
      <guid>https://dev.to/bellonedavide/how-to-use-ihttpclientfactory-and-wiremocknet-together-using-moq-2kl0</guid>
      <description>&lt;p&gt;Testing the integration with external HTTP clients can be a cumbersome task, but most of the time, it is necessary to ensure that a method is able to perform correct operations - not only sending the right information but also ensuring that we are able to read the content returned from the called API.&lt;/p&gt;

&lt;p&gt;Instead of spinning up a real server (even if in the local environment), we can simulate a connection to a mock server. A good library for creating temporary in-memory servers is WireMock.NET.&lt;/p&gt;

&lt;p&gt;Many articles I read online focus on creating a simple &lt;code&gt;HttpClient&lt;/code&gt;, using WireMock.NET to drive its behaviour. In this article, we are going to take a little step further: we are going to use WireMock.NET to handle &lt;code&gt;HttpClient&lt;/code&gt;s generated, using Moq, via &lt;code&gt;IHttpClientFactory&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explaining the dummy class used for the examples
&lt;/h2&gt;

&lt;p&gt;As per every practical article, we must start with a dummy example.&lt;/p&gt;

&lt;p&gt;For the sake of this article, I've created a dummy class with a single method that calls an external API to retrieve details of a book and then reads the returned content. If the call is successful, the method returns an instance of &lt;code&gt;Book&lt;/code&gt;; otherwise, it throws a &lt;code&gt;BookServiceException&lt;/code&gt; exception.&lt;/p&gt;

&lt;p&gt;Just for completeness, here's the &lt;code&gt;Book&lt;/code&gt; class:&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&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="n"&gt;Id&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="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Title&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's the &lt;code&gt;BookServiceException&lt;/code&gt; definition:&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="n"&gt;Serializable&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;BookServiceException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Exception&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;BookServiceException&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;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inner&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="k"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;BookServiceException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Serialization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SerializationInfo&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Serialization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StreamingContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&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;p&gt;Finally, we have our main class:&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BookService&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;IHttpClientFactory&lt;/span&gt; &lt;span class="n"&gt;_httpClientFactory&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;BookService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IHttpClientFactory&lt;/span&gt; &lt;span class="n"&gt;httpClientFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_httpClientFactory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpClientFactory&lt;/span&gt;&lt;span class="p"&gt;;&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetBookById&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;id&lt;/span&gt;&lt;span class="p"&gt;)&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;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"/api/books/&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="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_httpClientFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"books_client"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetFromJsonAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;&amp;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;return&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&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;BookServiceException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"There was an error while getting info about the book &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="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&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;p&gt;There are just two things to notice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We are injecting an instance of &lt;code&gt;IHttpClientFactory&lt;/code&gt; into the constructor.&lt;/li&gt;
&lt;li&gt;We are generating an instance of &lt;code&gt;HttpClient&lt;/code&gt; by &lt;strong&gt;passing a name to the &lt;code&gt;CreateClient&lt;/code&gt; method of &lt;code&gt;IHttpClientFactory&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we have our cards on the table, we can start!&lt;/p&gt;

&lt;h2&gt;
  
  
  WireMock.NET, a library to simulate HTTP calls
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://wiremock.org/" rel="noopener noreferrer"&gt;WireMock&lt;/a&gt; is an open-source platform you can install locally to create a real mock server. You can even create a cloud environment to generate and test HTTP endpoints.&lt;/p&gt;

&lt;p&gt;However, for this article we are interested in the &lt;strong&gt;NuGet package that takes inspiration from the WireMock project&lt;/strong&gt;, allowing .NET developers to generate disposable in-memory servers: &lt;code&gt;WireMock.NET&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To add the library, you must add the &lt;code&gt;WireMock.NET&lt;/code&gt; NuGet package to your project, for example using &lt;code&gt;dotnet add package WireMock.Net&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once the package is ready, you can generate a test server in your Unit Tests class:&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WireMockTests&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;WireMockServer&lt;/span&gt; &lt;span class="n"&gt;_server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;OneTimeSetUp&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;OneTimeSetUp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_server&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WireMockServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&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;SetUp&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;Setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Reset&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;OneTimeTearDown&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;OneTimeTearDown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Stop&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;You can instantiate a new instance of &lt;code&gt;WireMockServer&lt;/code&gt; in the &lt;code&gt;OneTimeSetUp&lt;/code&gt; step, store it in a private field, and make it accessible to every test in the test class.&lt;/p&gt;

&lt;p&gt;Before each test run, you can &lt;strong&gt;reset the internal status of the mock server&lt;/strong&gt; by running the &lt;code&gt;Reset()&lt;/code&gt; method. I'd suggest you reset the server to avoid unintentional internal status, but it all depends on what you want to do with the server instance.&lt;/p&gt;

&lt;p&gt;Finally, remember to free up resources by calling the &lt;code&gt;Stop()&lt;/code&gt; method in the &lt;code&gt;OneTimeTearDown&lt;/code&gt; phase (but not during the &lt;code&gt;TearDown&lt;/code&gt; phase: you still need the server to be on while running your tests!).&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic configuration of HTTP requests and responses with WireMock.NET
&lt;/h2&gt;

&lt;p&gt;The basic structure of the definition of a mock response using WireMock.NET is made of two parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Within the &lt;code&gt;Given&lt;/code&gt; method, you define the HTTP Verb and URL path whose response is going to be mocked.&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;RespondWith&lt;/code&gt;, you define what the mock server must return when the endpoint specified in the &lt;code&gt;Given&lt;/code&gt; step is called.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the next example, you can see that the &lt;code&gt;_server&lt;/code&gt; instance (the one I instantiated in the &lt;code&gt;OneTimeSetUp&lt;/code&gt; phase, remember?) must return a specific body (&lt;code&gt;responseBody&lt;/code&gt;) and the 200 HTTP Status Code when the &lt;code&gt;/api/books/42&lt;/code&gt; endpoint is called.&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="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;responseBody&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"
{
""Id"": 42,
""Title"": ""Life, the Universe and Everything""
}
"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;_server&lt;/span&gt;
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Given&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="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;WithPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/books/42"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;UsingGet&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RespondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithStatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;responseBody&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;Similarly, you can define that an endpoint will return an error by changing its status code:&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;_server&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Given&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="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;WithPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/books/42"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;UsingGet&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RespondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithStatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;404&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;All in all, both the request and the response are highly customizable: you can add HTTP Headers, delays, cookies, and much more.&lt;/p&gt;

&lt;p&gt;Look closely; there's one part that is missing: &lt;strong&gt;What is the full URL?&lt;/strong&gt; We have declared only the path (&lt;code&gt;/api/books/42&lt;/code&gt;) but have no info about the hostname and the port used to communicate.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to integrate WireMock.NET with a Moq-driven IHttpClientFactory
&lt;/h2&gt;

&lt;p&gt;In order to have WireMock.NET react to an HTTP call, we have to call the exact URL - even the hostname and port must match. But when we create a mocked &lt;code&gt;HttpClient&lt;/code&gt; - like we did &lt;a href="https://www.code4it.dev/blog/testing-httpclientfactory-moq/" rel="noopener noreferrer"&gt;in this article&lt;/a&gt; - we don't have a real hostname. So, how can we have WireMock.NET and &lt;code&gt;HttpClient&lt;/code&gt; work together?&lt;/p&gt;

&lt;p&gt;The answer is easy: since &lt;code&gt;WireMockServer.Start()&lt;/code&gt; automatically picks a free port on your localhost, you don't have to guess the port number, but you can reference the current instance of &lt;code&gt;_server&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once the &lt;code&gt;WireMockServer&lt;/code&gt; is created, &lt;strong&gt;internally it contains the reference to one or more URLs it will use to listen for HTTP requests&lt;/strong&gt;, intercepting the calls and &lt;strong&gt;replying in place of a real server&lt;/strong&gt;. You can then use one of these ports to configure the &lt;code&gt;HttpClient&lt;/code&gt; generated by the &lt;code&gt;HttpClientFactory&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's see the code:&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="n"&gt;Test&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;GetBookById_Should_HandleBadRequests&lt;/span&gt;&lt;span class="p"&gt;()&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;baseUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_server&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="n"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;myHttpClient&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;HttpClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;BaseAddress&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="n"&gt;baseUrl&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;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IHttpClientFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mockFactory&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;IHttpClientFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="n"&gt;mockFactory&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;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"books_client"&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;myHttpClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;_server&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Given&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="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;WithPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/books/42"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;UsingGet&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RespondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithStatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;BookService&lt;/span&gt; &lt;span class="n"&gt;service&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;BookService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CatchAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BookServiceException&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBookById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;42&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;First we access the base URL used by the mock server by accessing &lt;code&gt;_server.Url&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We use that URL as a base address for the newly created instance of &lt;code&gt;HttpClient&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, we create a mock of &lt;code&gt;IHttpClientFactory&lt;/code&gt; and configure it to return the local instance of &lt;code&gt;HttpClient&lt;/code&gt; whenever we call the &lt;code&gt;CreateClient&lt;/code&gt; method with the specified name.&lt;/p&gt;

&lt;p&gt;In the meantime, we define how the mock server must behave when an HTTP call to the specified path is intercepted.&lt;/p&gt;

&lt;p&gt;Finally, we can pass the instance of the mock &lt;code&gt;IHttpClientFactory&lt;/code&gt; to the &lt;code&gt;BookService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, the key part to remember is that you can simply access the &lt;code&gt;Url&lt;/code&gt; property (or, if you have configured it to handle many URLs, you can access the &lt;code&gt;Urls&lt;/code&gt; property, which is an array of strings).&lt;/p&gt;

&lt;h2&gt;
  
  
  Let WireMock.NET create the HttpClient for you
&lt;/h2&gt;

&lt;p&gt;As suggested by &lt;a href="https://github.com/StefH" rel="noopener noreferrer"&gt;Stef&lt;/a&gt; in the comments to this post, there's actually another way to generate the HttpClient with the correct URL: let WireMock.NET do it for you.&lt;/p&gt;

&lt;p&gt;Instead of doing&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="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;baseUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_server&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="n"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;myHttpClient&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;HttpClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;BaseAddress&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="n"&gt;baseUrl&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;you can simplify the process by calling the &lt;code&gt;CreateClient&lt;/code&gt; 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="n"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;myHttpClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, you will still have to pass the instance to the mock of &lt;code&gt;IHttpClientFactory&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further readings
&lt;/h2&gt;

&lt;p&gt;It's important to notice that &lt;strong&gt;WireMock and WireMock.NET are two totally distinct things&lt;/strong&gt;: one is a platform, and one is a library, owned by a different group of people, that mimics some functionalities from the platform to help developers write better tests.&lt;/p&gt;

&lt;p&gt;WireMock.NET is greatly integrated with many other libraries, such as xUnit, FluentAssertions, and .NET Aspire.&lt;/p&gt;

&lt;p&gt;You can find the official repository on GitHub:&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://github.com/WireMock-Net/WireMock.Net" rel="noopener noreferrer"&gt;WireMock.Net | Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article first appeared on &lt;a href="https://www.code4it.dev/" rel="noopener noreferrer"&gt;Code4IT 🐧&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It's important to remember that using an &lt;code&gt;HttpClientFactory&lt;/code&gt; is generally more performant than instantiating a new &lt;code&gt;HttpClient&lt;/code&gt;. Ever heard of &lt;em&gt;socket exhaustion&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://www.code4it.dev/csharptips/use-httpclientfactory-instead-of-httpclient/" rel="noopener noreferrer"&gt;Use IHttpClientFactory to generate HttpClient instances | Code4IT&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, for the sake of this article I've used Moq. However, there's a similar library you can use: NSubstitute. The learning curve is quite flat: in the most common scenarios, it's just a matter of syntax usage.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://www.code4it.dev/blog/moq-vs-nsubstitute-syntax/" rel="noopener noreferrer"&gt;Moq vs NSubstitute: syntax cheat sheet | Code4IT&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;In this article, we almost skipped all the basic stuff about WireMock.NET and tried to go straight to the point of integrating WireMock.NET with IHttpClientFactory.&lt;/p&gt;

&lt;p&gt;There are lots of articles out there that explain how to use WireMock.NET - just remember that WireMock and WireMock.NET are not the same thing!&lt;/p&gt;

&lt;p&gt;Ciao!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>moq</category>
      <category>httpclient</category>
      <category>unittest</category>
    </item>
    <item>
      <title>Davide's Code and Architecture Notes - Practical creation of C4 Model diagrams with Structurizr</title>
      <dc:creator>Davide Bellone</dc:creator>
      <pubDate>Tue, 17 Sep 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/bellonedavide/davides-code-and-architecture-notes-practical-creation-of-c4-model-diagrams-with-structurizr-3olh</link>
      <guid>https://dev.to/bellonedavide/davides-code-and-architecture-notes-practical-creation-of-c4-model-diagrams-with-structurizr-3olh</guid>
      <description>&lt;p&gt;The C4 Model is a well-known way of representing software architectures, describing the different parts via code in a hierarchical way: you start from the high-level view and gradually describe the details of each component and their interactions.&lt;/p&gt;

&lt;p&gt;I'm sure you've already heard of it. But have you ever used it in an actual project?&lt;/p&gt;

&lt;p&gt;I have used the C4 Model to describe the architecture of a - quite complex - project, and I decided to use Structurizr as a tool to generate such diagrams.&lt;/p&gt;

&lt;p&gt;In this article, I will share my experience with it, as well as some practical tips for using it. I have used Windows 11, but almost everything I share here can be used with other Operating Systems as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  An overview of the C4 Model
&lt;/h2&gt;

&lt;p&gt;The C4 Model is a framework, created by &lt;a href="https://x.com/simonbrown" rel="noopener noreferrer"&gt;Simon Brown&lt;/a&gt;, used in software architecture to provide a clear and structured way of describing complex software systems.&lt;/p&gt;

&lt;p&gt;Generating diagrams with different levels of abstraction provides users with various ways to look at how components are organized, how they communicate, and so on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://c4model.com/images/c4-overview.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.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%2F1cos4hqffpn5t9uka8oe.png" alt="C4 levels | C4Model.org" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It consists of &lt;strong&gt;four levels of abstraction&lt;/strong&gt;: Context, Containers, Components, and Code, each serving a distinct purpose.&lt;/p&gt;

&lt;h3&gt;
  
  
  Context: a high-level view, with external dependencies
&lt;/h3&gt;

&lt;p&gt;The Context level is the highest level of the model and provides a &lt;strong&gt;full-system view&lt;/strong&gt;, showing how the software system interacts with &lt;strong&gt;external entities&lt;/strong&gt; such as users, systems, and external services.&lt;/p&gt;

&lt;p&gt;By looking at this level, readers can see the external parts communicating with the software system.&lt;/p&gt;

&lt;p&gt;In general, here is where you want to &lt;strong&gt;describe the parts that are &lt;em&gt;not&lt;/em&gt; under your control&lt;/strong&gt;: if you store data in a database managed by somebody else, you can show it here. If you manage the database (even if it is in the cloud - as long as you have some control over it), you should list it at a lower level, like Containers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Containers: the internal architecture's main modules
&lt;/h3&gt;

&lt;p&gt;The Container level breaks down the system into its &lt;strong&gt;major modules&lt;/strong&gt;, which are typically applications, data stores, microservices, etc.&lt;/p&gt;

&lt;p&gt;This level outlines the high-level technology choices and how these containers interact with one another.&lt;/p&gt;

&lt;p&gt;Here, for example, you can describe all your microservices, listing the dependencies, the frameworks used to implement them, and so on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Components: the main parts within a Container
&lt;/h3&gt;

&lt;p&gt;The Component level describes &lt;strong&gt;the various modules and parts that are part of the parent Container&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;These components are detailed in a way that shows their responsibilities, interactions, and how they work together to fulfil the container's purpose.&lt;/p&gt;

&lt;p&gt;In the case of an application based on .NET, you can think of components as the different class libraries within the solution. However, &lt;strong&gt;this level works better if you think of the logical separation of the components, not the physical separation&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code: the low-level view of the code
&lt;/h3&gt;

&lt;p&gt;The most granular level of the C4 Model, the Code level, is often optional but, in some specific cases, it can be incredibly informative.&lt;/p&gt;

&lt;p&gt;It provides the &lt;strong&gt;implementation details of the system's components&lt;/strong&gt;, often visualized through class or interface diagrams that show the code structure and dependencies.&lt;/p&gt;

&lt;p&gt;From my experience, since the code changes frequently, the Code level risks being outdated; and since &lt;strong&gt;wrong info is worse than no info&lt;/strong&gt;, I generally choose not to include this level.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is (and what is not) the C4 Model
&lt;/h2&gt;

&lt;p&gt;The C4 Model is particularly beneficial for several reasons. It helps create a common language for team members to discuss architecture, &lt;strong&gt;facilitates the onboarding of new developers&lt;/strong&gt; by providing them with a clear system map, and helps in risk identification and threat modelling.&lt;/p&gt;

&lt;p&gt;Moreover, since it's based on abstractions, it's flexible enough to be used at various stages of development, from initial design to documenting existing systems.&lt;/p&gt;

&lt;p&gt;It's important to remember that &lt;strong&gt;C4 Model is a way of describing software. But it's not a Language, nor a Tool&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To generate a C4 Model, you can use many tools, like &lt;a href="https://structurizr.com/" rel="noopener noreferrer"&gt;Structurizr&lt;/a&gt; (created by Simon Brown, the creator of the C4 Model). Structurizr has its own DSL (&lt;em&gt;Domain Specific Language&lt;/em&gt;), but nothing stops you from creating C4 Model diagrams using any tool - even a simple handwritten sketch can be acceptable.&lt;/p&gt;

&lt;p&gt;Let's create the diagrams for a realistic application: a Web UI that shows the latest articles from a website.&lt;/p&gt;

&lt;p&gt;This project is composed of two main parts:&lt;/p&gt;

&lt;p&gt;A background job (something like an Azure Function) that accesses an RSS feed and stores the published articles' data on a DB.&lt;br&gt;
A web UI, accessed by users, that reads the info of the articles from the DB and shows them on screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F7sybt6wq2ck7jge7vca2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F7sybt6wq2ck7jge7vca2.png" alt="Dummy system architectural structure" width="489" height="371"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step-by-step C4 diagrams with Structurizr
&lt;/h2&gt;

&lt;p&gt;As we have seen before, the C4 Model is not a language or tool but a type of visual representation.&lt;/p&gt;

&lt;p&gt;In this article, we will use Structurizr. To generate diagrams with it, we have to use its specific syntax and have access to it (either in the cloud or locally).&lt;/p&gt;

&lt;p&gt;I have my project saved in a Desktop folder. Alongside the production code, I have a folder named "documentation" that stores everything related to - guess what? - docs. Within that folder, I have a "c4-diagrams" directory containing the diagrams and the C4 Model.&lt;/p&gt;
&lt;h3&gt;
  
  
  File structure for Structurizr
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Every "project" is described by its own workspace&lt;/strong&gt;, which contains everything needed to describe the different levels and modules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A workspace is nothing but a text file with the .dsl extension&lt;/strong&gt;. You can create a &lt;em&gt;workspace.dsl&lt;/em&gt; file in any directory (though I suggest you do that in a specific "documentation" folder).&lt;/p&gt;

&lt;p&gt;Since it's a plain text file, you can add it to source control - this simple tip allows your colleagues to collaborate with the diagram creation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F92bbvzg0lx3r09fcbgsg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F92bbvzg0lx3r09fcbgsg.png" alt="Example of how to store documentation on your file system" width="379" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To start with a very basic example, create a &lt;code&gt;workspace&lt;/code&gt; node within the &lt;em&gt;workspace.dsl&lt;/em&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workspace {
    model {
        mainSystem = softwareSystem "Latest Articles software system" {
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This workspace contains a &lt;code&gt;model&lt;/code&gt; node, that is used to list all the components and modules that are part of the workspace.&lt;/p&gt;

&lt;p&gt;In this case, we have only one item: &lt;code&gt;mainSystem&lt;/code&gt;, which is an object of type &lt;code&gt;softwareSystem&lt;/code&gt;, whose label is "Latest Articles software system".&lt;/p&gt;

&lt;p&gt;We will add some real items soon.&lt;/p&gt;

&lt;h3&gt;
  
  
  2 ways to install Structurizr locally (on Windows 11)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;You can use Structurizr on the cloud or locally&lt;/strong&gt;. I generally prefer having Structurizr Lite installed on my local machine so that I can generate the diagrams locally and, when necessary, modify them before saving the changes.&lt;/p&gt;

&lt;p&gt;To use it, have two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install it as a Java application (it requires Java 17+) running on a UNIX-based system: you must be able to run a Bash script to make it work;&lt;/li&gt;
&lt;li&gt;Install it via Docker: pull the Docker image and run the application as a Docker container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since I work with Windows, I prefer using Docker.&lt;/p&gt;

&lt;p&gt;To pull the image, you have to run the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;structurizr/lite&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are an absolute beginner with Docker, &lt;a href="https://www.code4it.dev/blog/run-mongodb-on-docker/" rel="noopener noreferrer"&gt;I published a short article&lt;/a&gt; that explains the main concepts without all the fuzz around it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run Structurizr locally with Docker
&lt;/h3&gt;

&lt;p&gt;Once you have pulled its Docker image, you can run it specifying the path to the Workspace file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;docker run -it --rm -p 8080:8080 -v C:/Users/d.bellone/Desktop/ProvaStructurizr/documentation/c4-diagrams:/usr/local/structurizr structurizr/lite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are some important things to notice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you have to use the full path to the &lt;em&gt;dsl&lt;/em&gt; file.&lt;/li&gt;
&lt;li&gt;the path separator must be &lt;code&gt;/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;if the workspace file does not exist, it will be created automatically with a default template.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, replace the local path with your own.&lt;/p&gt;

&lt;p&gt;Now, if everything goes well, you will be able to see the empty diagram on &lt;em&gt;localhost:8080&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F7ce7ofnf2v5blal868q6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F7ce7ofnf2v5blal868q6.png" alt="Structurizr UI accessible on Localhost" width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up the Context elements
&lt;/h3&gt;

&lt;p&gt;The first level of the C4 Model is the Context: here, you list all the top-level parts that are involved with the software systems, like external dependencies and users.&lt;/p&gt;

&lt;p&gt;First, we have the user who interacts with our system. We can call it "Reader".&lt;/p&gt;

&lt;p&gt;Then we have the RSS feed that we are integrating, which we can call "RSS Feed".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workspace {
    model {
        reader = person "Reader"
        mainSystem = softwareSystem "Latest Articles software system"
        rssFeed = softwareSystem "RSS Feed"
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can see the result on Structurizr:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fg967fzpth2m5isxh7jpr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fg967fzpth2m5isxh7jpr.png" alt="System level with main actors" width="800" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please note that the order in which the elements appear is the same as the order in which the elements are listed in the &lt;code&gt;model&lt;/code&gt; node.&lt;/p&gt;

&lt;p&gt;Also, note that three parts characterize each element:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the variable name, &lt;code&gt;reader&lt;/code&gt;, that allows us to reference this element in other parts of the diagram;&lt;/li&gt;
&lt;li&gt;the type of element, &lt;code&gt;person&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;the label associated with the element, &lt;code&gt;Reader&lt;/code&gt;, that is shown as box title.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Describe Containers and their interactions
&lt;/h3&gt;

&lt;p&gt;The Containers level describes the different modules of the software system.&lt;/p&gt;

&lt;p&gt;In our specific example, we have the following modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;Web Application&lt;/em&gt;, accessed by the user;&lt;/li&gt;
&lt;li&gt;The "&lt;em&gt;FetchJob&lt;/em&gt;" job application, that reads the content from the RSS Feed;&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;internal Database&lt;/em&gt;, that contains the data stored by the FetchJob and read by the Web Application;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these items are part of the "Latest Articles software system", so they need to be included as children of that node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workspace {
    model {
        reader = person "Reader"
        rssFeed = softwareSystem "RSS Feed"
        mainSystem = softwareSystem "Latest Articles software system" {
            webapplication = container "Web Application" {
            reader -&amp;gt; this "Reads Articles info"
            }
            fetchJob = container "FetchJob" {
                this -&amp;gt; rssFeed "Fetches RSS Feed"
            }
            database = container "Internal Database" {
                webapplication -&amp;gt; this "Reads articles info"
                fetchJob -&amp;gt; this "Stores articles info"
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that you can use the &lt;code&gt;this&lt;/code&gt; keyword to refer to the active element.&lt;/p&gt;

&lt;p&gt;Another important part to consider &lt;strong&gt;is that you cannot reference an element that hasn't already been declared&lt;/strong&gt;. That's why, compared with the previous example, I had to move the &lt;code&gt;rssFeed&lt;/code&gt; item before the &lt;code&gt;mainSystem&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that we have all the components and their interactions, we can see that the Context layer also shows the inbound and outbound interactions with the external systems.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fzo5iaauthas2y7gi2dr1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fzo5iaauthas2y7gi2dr1.png" alt="Context level with components interactions" width="470" height="1366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we look into the Components layer, we can see how the different components interact with each other and with the external sources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F58n8xq1f7wgxnzc5t4z4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F58n8xq1f7wgxnzc5t4z4.png" alt="Components level with interactions" width="800" height="684"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even with just these two levels, we have enough info to understand how the system behaves and what the main moving parts are.&lt;/p&gt;

&lt;h3&gt;
  
  
  Define Components
&lt;/h3&gt;

&lt;p&gt;Each container is made of one or more components.&lt;/p&gt;

&lt;p&gt;In our example, the Web Application is made of two components: the &lt;em&gt;Web UI&lt;/em&gt; and the &lt;em&gt;Backend Application&lt;/em&gt;, which interacts with the database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workspace {
    model {
        reader = person "Reader"
        rssFeed = softwareSystem "RSS Feed"
        mainSystem = softwareSystem "Latest Articles software system" {
            webapplication = container "Web Application" {
                ui = component "Web UI" {
                    reader -&amp;gt; this "Reads Articles info"
                }
                backend = component "Backend" {
                    ui -&amp;gt; this "Asks for data"
                }
            }
            fetchJob = container "FetchJob" {
                this -&amp;gt; rssFeed "Fetches RSS Feed"
            }
            database = container "Internal Database"{
            backend -&amp;gt; this "Reads articles info"
            fetchJob -&amp;gt; this "Stores articles info"
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have the UI and Backend components. Of course, since they describe the components within the web application in better detail, they replace the content within the &lt;code&gt;webapplication&lt;/code&gt; node.&lt;/p&gt;

&lt;p&gt;Also, the &lt;code&gt;backend&lt;/code&gt; node is now referenced within the &lt;code&gt;database&lt;/code&gt; container.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fr8yptn0pgolt8vq0bu23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fr8yptn0pgolt8vq0bu23.png" alt="Detailed components level with interaction" width="366" height="1366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Code level
&lt;/h3&gt;

&lt;p&gt;The Code level is used so rarely that the Structurizr DSL does not even include it.&lt;/p&gt;

&lt;p&gt;However, you can use other tools to generate the code diagrams and then reference those diagrams in the C4 Model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical tips for working with Structurizr
&lt;/h2&gt;

&lt;p&gt;Now that we have seen how to create the basic structure of a C4 Model with Structurizer, let's have a look at some practical tips that I found useful when describing the application I was working on. Of course, if you know some others, let me know in the comments section!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Store all the documentation in a single folder&lt;/strong&gt;: the more scattered the documentation across your systems, the harder it becomes to find what you are looking for and remember to keep everything up-to-date.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remember to follow the DSL grammar&lt;/strong&gt;: while the C4 Model is an abstract way to represent a software system, if you want to use Structurizr to generate such type of models, you have to &lt;a href="https://docs.structurizr.com/dsl/language" rel="noopener noreferrer"&gt;follow its DSL&lt;/a&gt;. It's a strict grammar, with some rules you'll get to know while working on it (like the fact that you cannot reference an item before it has been referenced).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use verbs in the active form&lt;/strong&gt;: instead of "Is called by", use "Calls", following the arrow direction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate by logical meaning, not physical division&lt;/strong&gt;: using as an example a .NET application, even if two functionalities (aka: Modules) are part of the same Class Library, you should represent them as separate entities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use tags and colours to mark external and internal components&lt;/strong&gt;: each element can be represented with a specific colour and can be tagged with custom values. Make use of these capabilities to help readers identify internal and external components.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use themes to customize how the diagram is rendered&lt;/strong&gt;: Structurizr allows you to &lt;a href="https://docs.structurizr.com/ui/diagrams/themes" rel="noopener noreferrer"&gt;apply themes&lt;/a&gt; to your diagram to change how the rendering looks like. For example, you can configure it to use Azure icons.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save your diagrams in GIT&lt;/strong&gt;: you want to keep track of the changes to your diagrams, ensuring that the description matches the actual structure of the system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update the .gitignore file&lt;/strong&gt;: even if you just update the &lt;em&gt;workspace.dsl&lt;/em&gt; file, Structurizr creates some temporary files (metadata, thumbnails, and so on), as you can see in the screenshot below. These files are stored in the &lt;code&gt;.structurizr&lt;/code&gt; folder. So, remember to add the &lt;code&gt;.structuriz&lt;/code&gt; folder to your gitignore file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ffeoadq107o2818hhq4fc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ffeoadq107o2818hhq4fc.png" alt="Dynamically modified files on local file system" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Split the diagram into different parts, and join them using "includes"&lt;/strong&gt;: when the application grows in a way that it becomes difficult to read and manage, you can create separate sub-diagrams and join them using the &lt;a href="https://docs.structurizr.com/dsl/includes" rel="noopener noreferrer"&gt;includes operator&lt;/a&gt;. For example, if you have a solution with 15 projects, you can create one diagram inside each of the 15 projects and join them all in a root diagram.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export the diagrams to other formats&lt;/strong&gt;: Structurizr is not the only tool you can use to analyze the structure of your system. You can export the diagram in different formats using &lt;a href="https://docs.structurizr.com/export/mermaid" rel="noopener noreferrer"&gt;Mermaid&lt;/a&gt; or &lt;a href="https://docs.structurizr.com/export/ilograph" rel="noopener noreferrer"&gt;Ilograph&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install the Structurizr extension on VSCode&lt;/strong&gt;: since Structurizr DSL is a custom language, it is not natively supported by the most popular IDEs. To have a nice syntax highlighting, you can install the Structurizr extension for VSCode, as seen below.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fx1g85digyio9fqznma7a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fx1g85digyio9fqznma7a.png" alt="Structurizr extension on VSCode" width="736" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Further readings
&lt;/h2&gt;

&lt;p&gt;As always, the best way to learn how something works is by trying it.&lt;/p&gt;

&lt;p&gt;You may want to reference the official documentation, provided by Simon Brown:&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://c4model.com/" rel="noopener noreferrer"&gt;C4 Model website&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article first appeared on &lt;a href="https://www.code4it.dev/" rel="noopener noreferrer"&gt;Code4IT 🐧&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Simon himself wrote an article that explains how to get started with it. It does not go into detail like the one you've just read, but since it's written by the tool author, I think it's a valuable read&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://dev.to/simonbrown/getting-started-with-structurizr-lite-27d0"&gt;Getting started with Structurizr Lite | Dev.TO&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another cool part about Structurizr is that you can use it to generate ADRs. You don't know what ADRs are? I've got you covered!&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://www.code4it.dev/architecture-notes/architecture-decision-records/" rel="noopener noreferrer"&gt;Tracking decision with Architecture Decision Records (ADRs)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;It's not necessary to use Structurizr to generate such C4 Model diagrams. However, it's probably one of the most complete tools you can find online.&lt;/p&gt;

&lt;p&gt;Remember, you should describe the architecture to the best level of detail: not too many details (or you'll lose time describing stuff with little to no value) nor too few (or you won't get the sense of the architecture). &lt;/p&gt;

</description>
      <category>architecture</category>
      <category>c4</category>
      <category>c4model</category>
      <category>structurizr</category>
    </item>
    <item>
      <title>C# Tip: ObservableCollection - a data type to intercept changes to the collection</title>
      <dc:creator>Davide Bellone</dc:creator>
      <pubDate>Tue, 27 Aug 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/bellonedavide/c-tip-observablecollection-a-data-type-to-intercept-changes-to-the-collection-811</link>
      <guid>https://dev.to/bellonedavide/c-tip-observablecollection-a-data-type-to-intercept-changes-to-the-collection-811</guid>
      <description>&lt;p&gt;Imagine you need a way to raise events whenever an item is added or removed from a collection.&lt;/p&gt;

&lt;p&gt;Instead of building a new class from scratch, you can use &lt;code&gt;ObservableCollection&amp;lt;T&amp;gt;&lt;/code&gt; to store items, raise events, and act when the internal state of the collection changes.&lt;/p&gt;

&lt;p&gt;In this article, we will learn how to use &lt;code&gt;ObservableCollection&amp;lt;T&amp;gt;&lt;/code&gt;, an out-of-the-box collection available in .NET.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the ObservableCollection type
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ObservableCollection&amp;lt;T&amp;gt;&lt;/code&gt; is a generic collection coming from the &lt;code&gt;System.Collections.ObjectModel&lt;/code&gt; namespace.&lt;/p&gt;

&lt;p&gt;It allows the most common operations, such as &lt;code&gt;Add&amp;lt;T&amp;gt;(T item)&lt;/code&gt; and &lt;code&gt;Remove&amp;lt;T&amp;gt;(T item)&lt;/code&gt;, as you can expect from most of the collections in .NET.&lt;/p&gt;

&lt;p&gt;Moreover, it implements two interfaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;INotifyCollectionChanged&lt;/code&gt; can be used to &lt;strong&gt;raise events when the internal collection is changed&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;INotifyPropertyChanged&lt;/code&gt; can be used to &lt;strong&gt;raise events when one of the properties of the changes&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see a simple example of the usage:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;collection&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;ObservableCollection&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;collection&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="s"&gt;"Mario"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;collection&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="s"&gt;"Luigi"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;collection&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="s"&gt;"Peach"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;collection&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="s"&gt;"Bowser"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Luigi"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;collection&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="s"&gt;"Waluigi"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Peach"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Move&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="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we can do all the basic operations: add, remove, swap items (with the &lt;code&gt;Move&lt;/code&gt; method), and check if the collection contains a specific value.&lt;/p&gt;

&lt;p&gt;You can &lt;strong&gt;simplify the initialization by passing a collection in the constructor&lt;/strong&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;collection&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;ObservableCollection&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="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Mario"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Luigi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Peach"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

 &lt;span class="n"&gt;collection&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="s"&gt;"Bowser"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

 &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Luigi"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

 &lt;span class="n"&gt;collection&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="s"&gt;"Waluigi"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

 &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Peach"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

 &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Move&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="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to intercept changes to the underlying collection
&lt;/h2&gt;

&lt;p&gt;As we said, this data type implements &lt;code&gt;INotifyCollectionChanged&lt;/code&gt;. Thanks to this interface, we can add &lt;strong&gt;event handlers&lt;/strong&gt; to the &lt;code&gt;CollectionChanged&lt;/code&gt; event and see what happens.&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;collection&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;ObservableCollection&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="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Mario"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Luigi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Peach"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CollectionChanged&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;WhenCollectionChanges&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Adding Bowser..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;collection&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="s"&gt;"Bowser"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&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;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Removing Luigi..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Luigi"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&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;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Adding Waluigi..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;collection&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="s"&gt;"Waluigi"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&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;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Searching for Peach..."&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;containsPeach&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Peach"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&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;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Swapping items..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Move&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="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;WhenCollectionChanges&lt;/code&gt; method accepts a &lt;code&gt;NotifyCollectionChangedEventArgs&lt;/code&gt; that gives you info about the intercepted changes:&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;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;WhenCollectionChanges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NotifyCollectionChangedEventArgs&lt;/span&gt; &lt;span class="n"&gt;e&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;allItems&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="n"&gt;Cast&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;ToArray&lt;/span&gt;&lt;span class="p"&gt;()&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;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;empty&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&amp;gt; Currently, the collection is &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;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;','&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allItems&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;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&amp;gt; The operation is &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Action&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;previousItems&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OldItems&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Cast&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;ToArray&lt;/span&gt;&lt;span class="p"&gt;()&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;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;empty&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&amp;gt; Before the operation it was &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;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;','&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;previousItems&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;currentItems&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewItems&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Cast&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;ToArray&lt;/span&gt;&lt;span class="p"&gt;()&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;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;empty&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&amp;gt; Now, it is &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;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;','&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;currentItems&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time an operation occurs, we write some logs.&lt;/p&gt;

&lt;p&gt;The result is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Adding Bowser...
&amp;gt; Currently, the collection is Mario,Luigi,Peach,Bowser
&amp;gt; The operation is Add
&amp;gt; Before the operation it was &amp;lt;empty&amp;gt;
&amp;gt; Now, it is Bowser

Removing Luigi...
&amp;gt; Currently, the collection is Mario,Peach,Bowser
&amp;gt; The operation is Remove
&amp;gt; Before the operation it was Luigi
&amp;gt; Now, it is &amp;lt;empty&amp;gt;

Adding Waluigi...
&amp;gt; Currently, the collection is Mario,Peach,Bowser,Waluigi
&amp;gt; The operation is Add
&amp;gt; Before the operation it was &amp;lt;empty&amp;gt;
&amp;gt; Now, it is Waluigi

Searching for Peach...

Swapping items...
&amp;gt; Currently, the collection is Mario,Bowser,Peach,Waluigi
&amp;gt; The operation is Move
&amp;gt; Before the operation it was Peach
&amp;gt; Now, it is Peach
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice a few points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;the &lt;code&gt;sender&lt;/code&gt; property holds the current items in the collection&lt;/strong&gt;. It's an &lt;code&gt;object?&lt;/code&gt;, so you have to cast it to another type to use it.&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;NotifyCollectionChangedEventArgs&lt;/code&gt; has different meanings depending on the operation:

&lt;ul&gt;
&lt;li&gt;when adding a value, &lt;code&gt;OldItems&lt;/code&gt; is null and &lt;code&gt;NewItems&lt;/code&gt; contains the items added during the operation;&lt;/li&gt;
&lt;li&gt;when removing an item, &lt;code&gt;OldItems&lt;/code&gt; contains the value just removed, and &lt;code&gt;NewItems&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;when swapping two items, both &lt;code&gt;OldItems&lt;/code&gt; and &lt;code&gt;NewItems&lt;/code&gt; contain the item you are moving.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to intercept when a collection property has changed
&lt;/h2&gt;

&lt;p&gt;To execute events when a property changes, we need to add a delegate to the &lt;code&gt;PropertyChanged&lt;/code&gt; event. However, it's not available directly on the &lt;code&gt;ObservableCollection&lt;/code&gt; type: you first have to cast it to an &lt;code&gt;INotifyPropertyChanged&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;collection&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;ObservableCollection&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="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Mario"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Luigi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Peach"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;INotifyPropertyChanged&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;PropertyChanged&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;WhenPropertyChanges&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Adding Bowser..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;collection&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="s"&gt;"Bowser"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&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;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Removing Luigi..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Luigi"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&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;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Adding Waluigi..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;collection&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="s"&gt;"Waluigi"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&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;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Searching for Peach..."&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;containsPeach&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Peach"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&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;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Swapping items..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Move&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="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now specify the &lt;code&gt;WhenPropertyChanges&lt;/code&gt; method as such:&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;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;WhenPropertyChanges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PropertyChangedEventArgs&lt;/span&gt; &lt;span class="n"&gt;e&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;allItems&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="n"&gt;Cast&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;ToArray&lt;/span&gt;&lt;span class="p"&gt;()&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;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;empty&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&amp;gt; Currently, the collection is &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;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;','&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allItems&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;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&amp;gt; Property &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PropertyName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; has changed"&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;As you can see, we have again the &lt;code&gt;sender&lt;/code&gt; parameter that contains the collection of items.&lt;/p&gt;

&lt;p&gt;Then, we have a parameter of type &lt;code&gt;PropertyChangedEventArgs&lt;/code&gt; that we can use to get the name of the property that has changed, using the &lt;code&gt;PropertyName&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;Let's run it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Adding Bowser...
&amp;gt; Currently, the collection is Mario,Luigi,Peach,Bowser
&amp;gt; Property Count has changed
&amp;gt; Currently, the collection is Mario,Luigi,Peach,Bowser
&amp;gt; Property Item[] has changed

Removing Luigi...
&amp;gt; Currently, the collection is Mario,Peach,Bowser
&amp;gt; Property Count has changed
&amp;gt; Currently, the collection is Mario,Peach,Bowser
&amp;gt; Property Item[] has changed

Adding Waluigi...
&amp;gt; Currently, the collection is Mario,Peach,Bowser,Waluigi
&amp;gt; Property Count has changed
&amp;gt; Currently, the collection is Mario,Peach,Bowser,Waluigi
&amp;gt; Property Item[] has changed

Searching for Peach...

Swapping items...
&amp;gt; Currently, the collection is Mario,Bowser,Peach,Waluigi
&amp;gt; Property Item[] has changed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, for every add/remove operation, we have two events raised: one to say that the &lt;code&gt;Count&lt;/code&gt; has changed, and one to say that the internal &lt;code&gt;Item[]&lt;/code&gt; is changed.&lt;/p&gt;

&lt;p&gt;However, notice what happens in the Swapping section: since you just change the order of the items, the &lt;code&gt;Count&lt;/code&gt; property does not change.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article first appeared on &lt;a href="https://www.code4it.dev/" rel="noopener noreferrer"&gt;Code4IT 🐧&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;As you probably noticed, &lt;strong&gt;events are fired after the collection has been initialized.&lt;/strong&gt; Clearly, it considers the items passed in the constructor as the initial state, and all the subsequent operations that mutate the state &lt;em&gt;can&lt;/em&gt; raise events.&lt;/p&gt;

&lt;p&gt;Also, notice that events are fired only if the reference to the value changes. If the collection holds more complex classes, like:&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&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="n"&gt;Name&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No event is fired if you change the value of the &lt;code&gt;Name&lt;/code&gt; property of an object already part of the collection:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;me&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;User&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;"Davide"&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;collection&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;ObservableCollection&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&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="k"&gt;new&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="n"&gt;me&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CollectionChanged&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;WhenCollectionChanges&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;INotifyPropertyChanged&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;PropertyChanged&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;WhenPropertyChanges&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;me&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;"Updated"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// It does not fire any event!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that &lt;strong&gt;&lt;code&gt;ObservableCollection&amp;lt;T&amp;gt;&lt;/code&gt; is not thread-safe&lt;/strong&gt;! You can find an &lt;a href="https://www.meziantou.net/thread-safe-observable-collection-in-dotnet.htm" rel="noopener noreferrer"&gt;interesting article&lt;/a&gt; by Gérald Barré (aka Meziantou) where he explains a thread-safe version of &lt;code&gt;ObservableCollection&amp;lt;T&amp;gt;&lt;/code&gt; he created. Check it out!&lt;/p&gt;

&lt;p&gt;As always, I suggest exploring the language and toying with the parameters, properties, data types, etc.&lt;/p&gt;

&lt;p&gt;You'll find lots of exciting things that may come in handy.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this article! Let's keep in touch on &lt;a href="https://twitter.com/BelloneDavide" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/BelloneDavide/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;! 🤜🤛&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;🐧&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>C# Tip: IFormattable interface, to define different string formats for the same object</title>
      <dc:creator>Davide Bellone</dc:creator>
      <pubDate>Tue, 27 Aug 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/bellonedavide/c-tip-iformattable-interface-to-define-different-string-formats-for-the-same-object-2b6o</link>
      <guid>https://dev.to/bellonedavide/c-tip-iformattable-interface-to-define-different-string-formats-for-the-same-object-2b6o</guid>
      <description>&lt;p&gt;Even when the internal data is the same, sometimes you can represent it in different ways. Think of the &lt;code&gt;DateTime&lt;/code&gt; structure: by using different modifiers, you can represent the same date in different formats.&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;DateTime&lt;/span&gt; &lt;span class="n"&gt;dt&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;DateTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2024&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="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;53&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dt&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="s"&gt;"yyyy-MM-dddd"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;//2024-01-Monday&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dt&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="s"&gt;"Y"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;//January 2024&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same datetime, different formats.&lt;/p&gt;

&lt;p&gt;You can further customise it by adding the &lt;code&gt;CultureInfo&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;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Globalization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CultureInfo&lt;/span&gt; &lt;span class="n"&gt;italianCulture&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;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Globalization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CultureInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"it-IT"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dt&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="s"&gt;"yyyy-MM-dddd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;italianCulture&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;//2024-01-lunedì&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dt&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="s"&gt;"Y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;italianCulture&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;//gennaio 2024&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, how can we use this behaviour in our custom classes?&lt;/p&gt;

&lt;h2&gt;
  
  
  IFormattable interface for custom ToString definition
&lt;/h2&gt;

&lt;p&gt;Take this simple POCO class:&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&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="n"&gt;FirstName&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="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;LastName&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="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;BirthDate&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can make this class implement the &lt;code&gt;IFormattable&lt;/code&gt; interface so that we can define and use the &lt;em&gt;advanced&lt;/em&gt; &lt;code&gt;ToString&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IFormattable&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="n"&gt;FirstName&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="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;LastName&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="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;BirthDate&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="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ToString&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;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IFormatProvider&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;formatProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Here, you define how to work with different formats&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;Now, we can define the different formats. Since I like to keep the available formats close to the main class, I added a nested class that only exposes the names of the formats.&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IFormattable&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="n"&gt;FirstName&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="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;LastName&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="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;BirthDate&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="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ToString&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;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IFormatProvider&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;formatProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Here, you define how to work with different formats&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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StringFormats&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;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;FirstAndLastName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"FL"&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;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Mini&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Mini"&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;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Full&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Full"&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;Finally, we can implement the &lt;code&gt;ToString(string? format, IFormatProvider? formatProvider)&lt;/code&gt; method, taking care of all the different formats we support (remember to handle the case when the format is not recognised!)&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;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ToString&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;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IFormatProvider&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;formatProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;StringFormats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstAndLastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&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;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{0} {1}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;StringFormats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Full&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;FormattableString&lt;/span&gt; &lt;span class="n"&gt;fs&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;FirstName&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;LastName&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;BirthDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;D&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fs&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="n"&gt;formatProvider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;StringFormats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mini&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Substring&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="m"&gt;1&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;LastName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Substring&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="m"&gt;1&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="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things to notice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I use a &lt;code&gt;switch&lt;/code&gt; statement based on the values defined in the &lt;code&gt;StringFormats&lt;/code&gt; subclass. If the format is empty or unrecognised, this method returns the default implementation of &lt;code&gt;ToString&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You can use whichever way to generate a string, like &lt;em&gt;string interpolation&lt;/em&gt;, or more complex ways;&lt;/li&gt;
&lt;li&gt;In the &lt;code&gt;StringFormats.Full&lt;/code&gt; branch, I stored the string format in a &lt;code&gt;FormattableString&lt;/code&gt; instance to apply the input &lt;code&gt;formatProvider&lt;/code&gt; to the final result.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Getting a custom string representation of an object
&lt;/h2&gt;

&lt;p&gt;We can try the different formatting options now that we have implemented them all.&lt;/p&gt;

&lt;p&gt;Look at how the behaviour changes based on the formatting and input culture (Hint: &lt;em&gt;venerdí&lt;/em&gt; is the Italian for &lt;em&gt;Friday&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="n"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person&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;Person&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;FirstName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Albert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;LastName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Einstein"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;BirthDate&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;DateTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1879&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Globalization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CultureInfo&lt;/span&gt; &lt;span class="n"&gt;italianCulture&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;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Globalization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CultureInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"it-IT"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&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="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringFormats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstAndLastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;italianCulture&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;//Albert Einstein&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&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="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringFormats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mini&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;italianCulture&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;//A.E&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&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="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringFormats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Full&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;italianCulture&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;//Albert Einstein (venerdì 14 marzo 1879)&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&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="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringFormats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Full&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="c1"&gt;//Albert Einstein (Friday, March 14, 1879)&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&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="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringFormats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Full&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CultureInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvariantCulture&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;//Albert Einstein (Friday, 14 March 1879)&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&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="s"&gt;"INVALID FORMAT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CultureInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvariantCulture&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;//Scripts.General.IFormattableTest+Person&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&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;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I am {0:Mini}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;//I am A.E&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"I am not &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Full&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="c1"&gt;//I am not Albert Einstein (Friday, March 14, 1879)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not only that, but &lt;strong&gt;now the result can also depend on the Culture related to the current thread&lt;/strong&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="k"&gt;using&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;TemporaryThreadCulture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;italianCulture&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&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="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringFormats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Full&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CultureInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentCulture&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Albert Einstein (venerdì 14 marzo 1879)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;using&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;TemporaryThreadCulture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;germanCulture&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&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="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringFormats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Full&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CultureInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentCulture&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;//Albert Einstein (Freitag, 14. März 1879)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(note: &lt;code&gt;TemporaryThreadCulture&lt;/code&gt; is a custom class that I explained in a previous article - see below)&lt;/p&gt;

&lt;h2&gt;
  
  
  Further readings
&lt;/h2&gt;

&lt;p&gt;You might be thinking «wow, somebody still uses &lt;code&gt;String.Format&lt;/code&gt;? Weird!»&lt;/p&gt;

&lt;p&gt;Well, even though it seems an old-style method to generate strings, it's still valid, as I explain here:&lt;/p&gt;

&lt;p&gt;🔗&lt;a href="https://www.code4it.dev/blog/how-to-use-string-format/" rel="noopener noreferrer"&gt;How to use String.Format - and why you should care about it | Code4IT&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, how did I temporarily change the culture of the thread? Here's how:&lt;br&gt;
🔗 &lt;a href="https://www.code4it.dev/csharptips/change-current-culture-in-using-block/" rel="noopener noreferrer"&gt;C# Tip: How to temporarily change the CurrentCulture | Code4IT&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article first appeared on &lt;a href="https://www.code4it.dev/" rel="noopener noreferrer"&gt;Code4IT 🐧&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;I hope you enjoyed this article! Let's keep in touch on &lt;a href="https://twitter.com/BelloneDavide" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/BelloneDavide/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;! 🤜🤛&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;🐧&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>How to create custom snippets in Visual Studio 2022</title>
      <dc:creator>Davide Bellone</dc:creator>
      <pubDate>Thu, 22 Aug 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/bellonedavide/how-to-create-custom-snippets-in-visual-studio-2022-4cea</link>
      <guid>https://dev.to/bellonedavide/how-to-create-custom-snippets-in-visual-studio-2022-4cea</guid>
      <description>&lt;p&gt;One of the best tricks to boost productivity is &lt;strong&gt;knowing your tools&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I'm pretty sure you've already used some predefined snippets in Visual Studio. For example, when you type &lt;em&gt;ctor&lt;/em&gt; and hit Tab twice, VS automatically creates an empty constructor for the current class.&lt;/p&gt;

&lt;p&gt;In this article, we will learn how to create custom snippets: in particular, we will design a snippet that automatically creates a C# Unit Test method with some placeholders and predefined Arrange-Act-Assert blocks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Snippet Designer: a Visual Studio 2022 extension to add a UI to your placeholders
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Snippets are defined in XML-like files with &lt;code&gt;.snippet&lt;/code&gt; extension&lt;/strong&gt;. But we all know that working with XMLs can be cumbersome, especially if you don't have a clear idea of the expected structure.&lt;/p&gt;

&lt;p&gt;Therefore, even if not strictly necessary, I suggest installing a VS2022 extension called &lt;em&gt;Snippet Designer 2022&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fktlgusdc20pbkvwzdjip.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fktlgusdc20pbkvwzdjip.png" alt="Snippet Designer 2022 in VS2022" width="800" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This extension, developed by Matthew Manela, can be found on &lt;a href="https://github.com/mmanela/SnippetDesigner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, where you can view the source code.&lt;/p&gt;

&lt;p&gt;This extension gives you a UI to customize the snippet instead of manually editing the XML nodes. It allows you to customize the snippet, the related metadata, and even the placeholders.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a basic snippet in VS2022 using a .snippet file
&lt;/h2&gt;

&lt;p&gt;As we saw, snippets are defined in a simple XML.&lt;/p&gt;

&lt;p&gt;In order to have your snippets immediately available in Visual Studio, I suggest you create those files in a specific VS2022 folder under the path &lt;code&gt;\Documents\Visual Studio 2022\Code Snippets\Visual C#\My Code Snippets\&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, create an empty file, change its extension to &lt;code&gt;.snippet&lt;/code&gt;, and save it to that location.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fjkz1r1iv5irzpq77ikkb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fjkz1r1iv5irzpq77ikkb.png" alt="Save snippet file under the My Code Snippets folder in VS2022" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you can open Visual Studio (it's not necessary to open a project, but I'd recommend you to do so). Then, head to &lt;em&gt;File &amp;gt; Open&lt;/em&gt;, and open the file you saved under the My Code Snippets directory.&lt;/p&gt;

&lt;p&gt;Thanks to Snippet Designer, you will be able to see a nice UI instead of plain XML content.&lt;/p&gt;

&lt;p&gt;Have a look at how I filled in the several parts to create a snippet that generates a variable named &lt;code&gt;x&lt;/code&gt;, assigns to it a value, and then calls &lt;code&gt;x++&lt;/code&gt;;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F25hf7d3i04ay4edwve2j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F25hf7d3i04ay4edwve2j.png" alt="Simple snippet, with related metadata and annotations" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have a look at the main parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;strong&gt;body&lt;/strong&gt;, which contains the snippet to be generated;&lt;/li&gt;
&lt;li&gt;the top layer, where we specified:

&lt;ul&gt;
&lt;li&gt;the Snippet name: Int100; it's the display name of the shortcut&lt;/li&gt;
&lt;li&gt;the code language: C#;&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;shortcut&lt;/strong&gt;: int100; it's the string you'll type in that allows you to generate the expected snippet;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;the bottom table, which contains the &lt;strong&gt;placeholders&lt;/strong&gt; used in the snippet; more on this later;&lt;/li&gt;

&lt;li&gt;the &lt;strong&gt;properties&lt;/strong&gt; tab, on the sidebar: here is where you specify some additional metadata, such as:

&lt;ul&gt;
&lt;li&gt;Author, Description, and Help Url of the snippet, in case you want to export it;&lt;/li&gt;
&lt;li&gt;the kind of snippet: possible values are MethodBody, MethodDecl and TypeDecl. However, this value is supported only in Visual Basic.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Now, hit save and be ready to import it!&lt;/p&gt;

&lt;p&gt;Just for completeness, here's the resulting XML:&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="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;CodeSnippets&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;CodeSnippet&lt;/span&gt; &lt;span class="na"&gt;Format=&lt;/span&gt;&lt;span class="s"&gt;"1.0.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Header&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;SnippetTypes&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;SnippetType&amp;gt;&lt;/span&gt;Expansion&lt;span class="nt"&gt;&amp;lt;/SnippetType&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/SnippetTypes&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Title&amp;gt;&lt;/span&gt;Int100&lt;span class="nt"&gt;&amp;lt;/Title&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Author&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/Author&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Description&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/Description&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;HelpUrl&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/HelpUrl&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Shortcut&amp;gt;&lt;/span&gt;int100&lt;span class="nt"&gt;&amp;lt;/Shortcut&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Snippet&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Code&lt;/span&gt; &lt;span class="na"&gt;Kind=&lt;/span&gt;&lt;span class="s"&gt;"method decl"&lt;/span&gt; &lt;span class="na"&gt;Language=&lt;/span&gt;&lt;span class="s"&gt;"csharp"&lt;/span&gt; &lt;span class="na"&gt;Delimiter=&lt;/span&gt;&lt;span class="s"&gt;"$"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;![CDATA[int x = 100;
x++;]]&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/Code&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Snippet&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/CodeSnippet&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/CodeSnippets&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that &lt;strong&gt;the actual content of the snippet is defined in the &lt;code&gt;CDATA&lt;/code&gt; block&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Import the snippet in Visual Studio
&lt;/h2&gt;

&lt;p&gt;It's time to import the snippet. Open the Tools menu item and click on Code Snippets Manager.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fr6aoh56lwg1gz6y1mcrw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fr6aoh56lwg1gz6y1mcrw.png" alt="Code Snippets Manager menu item, under Tools" width="370" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here, you can import a snippet by clicking the &lt;em&gt;Import...&lt;/em&gt; button. Given that we've already saved our snippet in the correct folder, we'll find it under the My Code Snippets folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fa71zgj3q6pg4m4siqfvk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fa71zgj3q6pg4m4siqfvk.png" alt="Code Snippets Manager tool" width="613" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it's ready! Open a C# class, and start typing &lt;code&gt;int100&lt;/code&gt;. You'll see our snippet in the autocomplete list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fiea2imq49mj7z0u0kbmj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fiea2imq49mj7z0u0kbmj.png" alt="Int100 snippet is now visible in Visual Studio" width="589" height="115"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By hitting Tab twice, you'll see the snippet's content being generated.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use placeholders when defining snippets in Visual Studio
&lt;/h2&gt;

&lt;p&gt;Wouldn't it be nice to have the possibility to define customizable parts of your snippets?&lt;/p&gt;

&lt;p&gt;Let's see a real example: I want to create a snippet to create the structure of a Unit Tests method with these characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it already contains the AAA (Arrange, Act, Assert) sections;&lt;/li&gt;
&lt;li&gt;the method name should follow the pattern "SOMETHING should DO STUFF when CONDITION". I want to be able to replace the different parts of the method name by using placeholders.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;You can define placeholders using the &lt;code&gt;$&lt;/code&gt; symbol&lt;/strong&gt;. You will then see the placeholders in the table at the bottom of the UI. In this example, the placeholders are &lt;code&gt;$TestMethod$&lt;/code&gt;, &lt;code&gt;$DoSomething$&lt;/code&gt;, and &lt;code&gt;$Condition$&lt;/code&gt;. I also added a description to explain the purpose of each placeholder better.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fx0m54965mjob5n4rgexr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fx0m54965mjob5n4rgexr.png" alt="TestSync snippet definition and metadata" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The XML looks like this:&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="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;CodeSnippets&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;CodeSnippet&lt;/span&gt; &lt;span class="na"&gt;Format=&lt;/span&gt;&lt;span class="s"&gt;"1.0.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Header&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;SnippetTypes&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;SnippetType&amp;gt;&lt;/span&gt;Expansion&lt;span class="nt"&gt;&amp;lt;/SnippetType&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/SnippetTypes&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Title&amp;gt;&lt;/span&gt;Test Sync&lt;span class="nt"&gt;&amp;lt;/Title&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Author&amp;gt;&lt;/span&gt;Davide Bellone&lt;span class="nt"&gt;&amp;lt;/Author&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Description&amp;gt;&lt;/span&gt;Scaffold the AAA structure for synchronous NUnit tests&lt;span class="nt"&gt;&amp;lt;/Description&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;HelpUrl&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/HelpUrl&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Shortcut&amp;gt;&lt;/span&gt;testsync&lt;span class="nt"&gt;&amp;lt;/Shortcut&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Snippet&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Declarations&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Literal&lt;/span&gt; &lt;span class="na"&gt;Editable=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;ID&amp;gt;&lt;/span&gt;TestMethod&lt;span class="nt"&gt;&amp;lt;/ID&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;ToolTip&amp;gt;&lt;/span&gt;Name of the method to be tested&lt;span class="nt"&gt;&amp;lt;/ToolTip&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;Default&amp;gt;&lt;/span&gt;TestMethod&lt;span class="nt"&gt;&amp;lt;/Default&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;Function&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/Function&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/Literal&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Literal&lt;/span&gt; &lt;span class="na"&gt;Editable=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;ID&amp;gt;&lt;/span&gt;DoSomething&lt;span class="nt"&gt;&amp;lt;/ID&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;ToolTip&amp;gt;&lt;/span&gt;Expected behavior or result&lt;span class="nt"&gt;&amp;lt;/ToolTip&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;Default&amp;gt;&lt;/span&gt;DoSomething&lt;span class="nt"&gt;&amp;lt;/Default&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;Function&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/Function&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/Literal&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Literal&lt;/span&gt; &lt;span class="na"&gt;Editable=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;ID&amp;gt;&lt;/span&gt;Condition&lt;span class="nt"&gt;&amp;lt;/ID&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;ToolTip&amp;gt;&lt;/span&gt;Initial conditions&lt;span class="nt"&gt;&amp;lt;/ToolTip&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;Default&amp;gt;&lt;/span&gt;Condition&lt;span class="nt"&gt;&amp;lt;/Default&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;Function&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/Function&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/Literal&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/Declarations&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Code&lt;/span&gt; &lt;span class="na"&gt;Language=&lt;/span&gt;&lt;span class="s"&gt;"csharp"&lt;/span&gt; &lt;span class="na"&gt;Delimiter=&lt;/span&gt;&lt;span class="s"&gt;"$"&lt;/span&gt; &lt;span class="na"&gt;Kind=&lt;/span&gt;&lt;span class="s"&gt;"method decl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;![CDATA[[Test]
public void $TestMethod$_Should_$DoSomething$_When_$Condition$()
{
    // Arrange

    // Act

    // Assert

}]]&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/Code&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Snippet&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/CodeSnippet&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/CodeSnippets&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, import it as we already did before.&lt;/p&gt;

&lt;p&gt;Then, head to your code, start typing &lt;code&gt;testsync&lt;/code&gt;, and you'll see the snippet come to life. The placeholders we defined are highlighted. You can then fill in these placeholders, hit tab, and move to the next one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F4vyerjrf9qol2cuibgjk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F4vyerjrf9qol2cuibgjk.gif" alt="Test sync snippet usage" width="570" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: how to view all the snippets defined in VS
&lt;/h2&gt;

&lt;p&gt;If you want to learn more about your IDE and the available snippets, you can have a look at the Snippet Explorer table.&lt;/p&gt;

&lt;p&gt;You can find it under &lt;em&gt;View&lt;/em&gt; &amp;gt; &lt;em&gt;Tools&lt;/em&gt; &amp;gt; &lt;em&gt;Snippet Explorer&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F7n1g4s782jvyst7t9mer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F7n1g4s782jvyst7t9mer.png" alt="Snippet Explorer menu item" width="662" height="714"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, you can see all the snippets, their shortcuts, and the content of each snippet. You can also see the placeholders highlighted in green.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F4804jgtt6j3as2kd3grl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F4804jgtt6j3as2kd3grl.png" alt="List of snippets available in Snippet Explorer" width="800" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's always an excellent place to learn more about Visual Studio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further readings
&lt;/h2&gt;

&lt;p&gt;As always, you can read more on Microsoft Docs. It's a valuable resource, although I find it difficult to follow.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://learn.microsoft.com/en-us/visualstudio/ide/walkthrough-creating-a-code-snippet?view=vs-2022&amp;amp;wt.mc_id=DT-MVP-5005077" rel="noopener noreferrer"&gt;Create a code snippet in Visual Studio | Microsoft docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I prefer working with the UI. If you want to have a look at the repo of the extension we used in this article, here's the link:&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://github.com/mmanela/SnippetDesigner/" rel="noopener noreferrer"&gt;SnippetDesigner extension | GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article first appeared on &lt;a href="https://www.code4it.dev/" rel="noopener noreferrer"&gt;Code4IT 🐧&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;There are some tips that may improve both the code quality and the developer productivity.&lt;/p&gt;

&lt;p&gt;If you want to enforce some structures or rules, add such snippets in your repository; when somebody joins your team, teach them how to import those snippets.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this article! Let's keep in touch on &lt;a href="https://twitter.com/BelloneDavide" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/BelloneDavide/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;! 🤜🤛&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;🐧&lt;/p&gt;

</description>
      <category>visualstudio</category>
      <category>vs2022</category>
    </item>
  </channel>
</rss>
