<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Grant Riordan - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Grant Riordan - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 18 May 2026 22:34:09 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/grantdotdev/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Query Data in DynamoDB Using .Net ]]>
                </title>
                <description>
                    <![CDATA[ If you're coming to DynamoDB from a relational background, the first thing to understand is this: it's a completely different way of thinking. DynamoDB isn't a relational database, it's a NoSQL key-va ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-query-data-in-dynamodb-using-net/</link>
                <guid isPermaLink="false">69fa1ffca386d7f121b4955b</guid>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                    <category>
                        <![CDATA[ DynamoDB ]]>
                    </category>
                
                    <category>
                        <![CDATA[ dotnet ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Tue, 05 May 2026 16:51:08 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/66b52b176a1b17f6b28d9822/93c4db14-f870-47d6-99c9-e0816d8b628b.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you're coming to DynamoDB from a relational background, the first thing to understand is this: it's a completely different way of thinking.</p>
<p>DynamoDB isn't a relational database, it's a NoSQL key-value and document store. You don't write arbitrary queries against your data. Instead, you design your tables around the specific access patterns your application needs.</p>
<p>DynamoDB is driven by your queries, not your data.</p>
<p>There's no need for joins or heavy normalisation. To get the performance DynamoDB is built for, model your data so it can be retrieved efficiently using keys – partition keys and sort keys – rather than relying on table scans or complex query logic.</p>
<p>If you try to use DynamoDB like SQL, it will fight you — and you will lose.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>There are a few things you'll need and some general knowledge that you should have to follow along most effectively here:</p>
<p><strong>AWS</strong></p>
<ul>
<li><p>An active AWS account with permissions to create and modify DynamoDB</p>
</li>
<li><p>Basic familiarity with the AWS Console (navigating services, not deep expertise)</p>
</li>
</ul>
<p><strong>C# / .NET</strong></p>
<ul>
<li><p>Comfortable with C#</p>
</li>
<li><p>Dependency injection</p>
</li>
<li><p>NuGet package management — you'll need to install <code>AWSSDK.DynamoDBv2</code></p>
</li>
</ul>
<p><strong>Databases (Conceptual)</strong></p>
<ul>
<li>(Optional) A working understanding of relational databases / SQL is actually helpful here — the article explicitly addresses readers coming from that background and explains the mental shift required</li>
</ul>
<p><strong>What you don't need</strong></p>
<ul>
<li><p>Prior DynamoDB experience — the article covers core concepts from scratch</p>
</li>
<li><p>Deep AWS infrastructure knowledge — IAM, VPCs, and so on aren't covered</p>
</li>
</ul>
<p><strong>Optional but useful</strong></p>
<ul>
<li><p>AWS CLI installed locally if you want to follow the AWS CLI examples directly</p>
</li>
<li><p>Terraform experience if following the infrastructure-as-code example (Terraform section can be skipped without losing context)</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table Of Contents</h2>
<ul>
<li><p><a href="#heading-core-dynamodb-concepts">Core DynamoDB Concepts</a></p>
<ul>
<li><p><a href="#heading-partition-key">Partition Key</a></p>
</li>
<li><p><a href="#heading-sort-key-optional">Sort Key (Optional)</a></p>
</li>
<li><p><a href="#heading-global-secondary-index-gsi">Global Secondary Index (GSI)</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-synthetic-keys-the-old-way">Synthetic Keys — The Old Way</a></p>
</li>
<li><p><a href="#heading-multi-attribute-gsis-the-new-way">Multi-Attribute GSIs — The New Way</a></p>
<ul>
<li><p><a href="#heading-defining-a-multi-attribute-gsi">Defining a Multi-Attribute GSI</a></p>
</li>
<li><p><a href="#heading-query-rules-you-must-follow">Query Rules You Must Follow</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-c-sdk-options">C# SDK Options</a></p>
<ul>
<li><p><a href="#heading-low-level-client">Low-Level Client</a></p>
</li>
<li><p><a href="#heading-document-model">Document Model</a></p>
</li>
<li><p><a href="#heading-object-persistence-model">Object Persistence Model</a></p>
</li>
<li><p><a href="#heading-setting-up-the-context">Setting Up the Context</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-querying-a-multi-attribute-gsi-from-c">Querying a Multi-Attribute GSI from C</a></p>
</li>
<li><p><a href="#heading-query-vs-scan">Query vs Scan</a></p>
<ul>
<li><p><a href="#heading-query">Query</a></p>
</li>
<li><p><a href="#heading-scan">Scan</a></p>
</li>
<li><p><a href="#heading-when-is-a-scan-acceptable">When Is A Scan Acceptable?</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-filter-expressions">Filter Expressions</a></p>
</li>
<li><p><a href="#heading-paging-results-and-user-interfaces">Paging Results and User Interfaces</a></p>
<ul>
<li><p><a href="#heading-how-it-works-in-dynamodb">How It Works in DynamoDB</a></p>
</li>
<li><p><a href="#heading-how-this-works-in-the-dynamodb-c-sdk">How This Works In The DynamoDB C# SDK</a></p>
</li>
<li><p><a href="#heading-manual-pagination-the-right-approach-for-uis">Manual Pagination — The Right Approach For UIs</a></p>
</li>
<li><p><a href="#heading-what-about-go-to-to-page-7-navigation">What About "go to to page 7" Navigation?</a></p>
</li>
<li><p><a href="#heading-the-filterexpression-trap">The FilterExpression Trap</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-final-thoughts-amp-conclusion">Final Thoughts &amp; Conclusion</a></p>
</li>
</ul>
<h2 id="heading-core-dynamodb-concepts">Core DynamoDB Concepts</h2>
<p>DynamoDB is built around a few core concepts that directly influence how you query and structure your data.</p>
<p>Remember: DynamoDB is driven by how you retrieve your data, not by the shape of the data itself.</p>
<h3 id="heading-partition-key">Partition Key</h3>
<ul>
<li><p>Required for every query</p>
</li>
<li><p>Required to create a table – each table must have a partition key</p>
</li>
<li><p>Determines data distribution and how items are stored</p>
</li>
</ul>
<p>Data in DynamoDB is physically distributed across partitions. The partition key decides where a given item lives, which is why it's critical for both performance and scalability.</p>
<p>You can think of a partition key like a filing cabinet drawer label: it tells DynamoDB which drawer to open so it can go straight to your data without searching every drawer.</p>
<p>Partition keys are typically strings or numbers.</p>
<h3 id="heading-sort-key-optional">Sort Key (Optional)</h3>
<p>Also known as the <strong>Range Key</strong>.</p>
<ul>
<li>Enables range queries (<code>between</code>, <code>begins_with</code>, and so on)</li>
</ul>
<p>When you add a sort key, items with the same partition key are grouped together and ordered by the sort key. For string sort keys, ordering is lexicographical (dictionary order). For numeric sort keys, ordering is numeric (ascending).</p>
<p>This has an important effect when working with dates. If you store dates as strings in a non-ISO format (for example, <code>DD/MM/YYYY</code>), they won't sort in chronological order. For example, the resulting sorted items would look like this:</p>
<pre><code class="language-plaintext">01/01/2026
01/02/2026
01/03/2026
08/01/2026
15/02/2026
</code></pre>
<p>Here, all dates starting with <code>01</code> are grouped together, even though they span different months. This breaks range queries and ordering.</p>
<p>To avoid this, use either:</p>
<ul>
<li><p><strong>ISO 8601 format</strong> (<code>YYYY-MM-DD</code>), which sorts correctly as a string</p>
</li>
<li><p><strong>Unix timestamps</strong> (typically milliseconds since January 1st, 1970), which sort numerically and correctly</p>
</li>
</ul>
<p>Both approaches ensure your data is ordered correctly and can be queried efficiently. This is commonly used for timestamps, versioning, or logical groupings (for example, <code>ORDER#2024-01</code>).</p>
<h3 id="heading-global-secondary-index-gsi">Global Secondary Index (GSI)</h3>
<p>A Global Secondary Index (GSI) allows you to query your data using a different partition key and optional sort key than your base table. This is how you support additional access patterns without redesigning your primary key schema.</p>
<p>For example, if your base table is keyed by <code>UserId</code>, but you also need to query by <code>OrderId</code>, a GSI makes that possible.</p>
<h4 id="heading-projection-types">Projection Types</h4>
<p>A GSI doesn't have to include all attributes from the base table. When configuring your GSI you can choose:</p>
<ul>
<li><p><strong>ALL</strong> — all attributes are projected</p>
</li>
<li><p><strong>KEYS_ONLY</strong> — only index and primary keys</p>
</li>
<li><p><strong>INCLUDE</strong> — a subset of selected attributes</p>
</li>
</ul>
<p>Choosing the right projection helps reduce storage and read costs. For example, for an orders view you could project only <code>orderNumber</code>, <code>dateOrdered</code>, and <code>cost</code> rather than all other attributes which aren't needed.</p>
<h4 id="heading-important-considerations">Important Considerations</h4>
<p><strong>Additional cost:</strong> GSIs consume their own read/write capacity and storage in addition to your base table's costs. Using <code>ProjectionType = INCLUDE</code> or <code>KEYS_ONLY</code> instead of <code>ALL</code> reduces the storage cost of the GSI since less data is duplicated into the index, which can offset the additional read/write cost.</p>
<p><strong>Eventual consistency:</strong> In DynamoDB, when you write directly to the base table you have the option to perform a strongly consistent read immediately after — meaning you're guaranteed to get the latest data. GSIs don'r support this. GSI reads are always eventually consistent.</p>
<p>When a customer places an order, DynamoDB writes that order to your main Orders table. It then replicates that change to any GSIs asynchronously in the background. During that brief window (typically milliseconds), a query against a GSI may not return the newly written order yet.</p>
<p>A real-world example of where this can catch you out:</p>
<ol>
<li><p>Customer places an order which is written to the Orders table</p>
</li>
<li><p>User is redirected to the "Your Orders" page</p>
</li>
<li><p>"Your Orders" page queries a GSI for all orders by this customer</p>
</li>
<li><p>New order doesn't appear yet — GSI replication is still in progress</p>
</li>
<li><p>Customer refreshes the page 50ms later</p>
</li>
<li><p>Order now appears</p>
</li>
</ol>
<p>For most queries — browsing a product catalogue, viewing order history, filtering by status — this is completely acceptable and unnoticeable to the user. Where it matters is when your application writes a record and immediately queries a GSI for that same record. In this scenario you have a couple of options:</p>
<ul>
<li><p>Query the base table directly after a write using a strongly consistent read, rather than the GSI</p>
</li>
<li><p>Pass the order data directly to the UI from the write response, without a follow-up query at all — the cleanest solution in most cases</p>
</li>
</ul>
<p><strong>Write amplification:</strong> Every write to the base table may also write to one or more GSIs. GSIs are powerful, but they're not free. Overusing them is often a sign that your primary access patterns weren't well defined upfront.</p>
<p><strong>Note:</strong> DynamoDB allows a maximum of 20 GSIs per table by default, though this can be increased via an AWS service limit request.</p>
<h2 id="heading-synthetic-keys-the-old-way">Synthetic Keys — The Old Way</h2>
<p>Before we look at multi-attribute GSIs, it's worth understanding the pattern they replace — because you'll encounter it in existing DynamoDB codebases.</p>
<p>Imagine you want to query orders by both status and date — for example, all "pending" orders placed in the last 30 days. Previously, DynamoDB GSIs only supported a single attribute as the partition key and a single attribute as the sort key. To filter on multiple attributes you had to combine them into a single synthetic attribute:</p>
<pre><code class="language-csharp">[DynamoDBTable("Orders")]
public class OrderDto
{
    [DynamoDBHashKey("customerId")]
    public string CustomerId { get; set; }

    [DynamoDBRangeKey("createdAt")]
    public long CreatedAt { get; set; }

    [DynamoDBProperty("orderId")]
    public string OrderId { get; set; }

    [DynamoDBProperty("status")]
    public string Status { get; set; }

    // Synthetic key — manually constructed before saving
    [DynamoDBProperty("statusDate")]
    public string StatusDate { get; set; } // e.g. "PENDING#2025-11-01"
}
</code></pre>
<p>Constructing this value before saving the record:</p>
<pre><code class="language-csharp">order.StatusDate = $"{order.Status}#{order.CreatedAt:yyyy-MM-dd}";
</code></pre>
<p>Then create a GSI on <code>statusDate</code> as the partition key, allowing you to query:</p>
<pre><code class="language-csharp">var results = await _context.QueryAsync&lt;OrderDto&gt;(
    "PENDING#2025-11-01",
    config // IndexName = "statusDate-index"
).GetRemainingAsync();
</code></pre>
<p>This worked, but came with real downsides:</p>
<ul>
<li><p><strong>Brittle</strong> — every developer writing to the table must know about and correctly format the synthetic key</p>
</li>
<li><p><strong>Hard to query ranges</strong> — filtering all pending orders across a date range required careful <code>begins_with</code> or <code>between</code> conditions on a concatenated string</p>
</li>
<li><p><strong>Maintenance overhead</strong> — if status values change, every existing record needs updating</p>
</li>
<li><p><strong>Invisible in the schema</strong> — a new developer has no idea what <code>statusDate</code> means without documentation</p>
</li>
<li><p><strong>Backfilling</strong> — adding a new synthetic-key GSI to an existing table meant updating every existing record to populate the new attribute via a script re-processing the existing items.</p>
</li>
</ul>
<h2 id="heading-multi-attribute-gsis-the-new-way">Multi-Attribute GSIs — The New Way</h2>
<p>On November 19, 2025, AWS <a href="https://aws.amazon.com/about-aws/whats-new/2025/11/amazon-dynamodb-multi-attribute-composite-keys-global-secondary-indexes/">announced multi-attribute composite keys for GSIs</a>. You can now define a GSI partition key or sort key comprised of up to <strong>4 attributes each</strong> — <strong>8 attributes in total</strong> across the partition and sort key combined.</p>
<p>A few important things to note:</p>
<ul>
<li><p><strong>GSIs only:</strong> This applies to GSIs only — your base table primary key structure is unchanged, still a single partition key and an optional single sort key.</p>
</li>
<li><p><strong>DynamoDB handles composition internally:</strong> You don't concatenate values yourself. DynamoDB hashes the partition key attributes together for data distribution, and maintains hierarchical sort order across the sort key attributes.</p>
</li>
<li><p><strong>Strict query rules still apply:</strong> You must supply <strong>all</strong> partition key attributes with equality conditions when querying. Sort key attributes must be queried <strong>left-to-right</strong> in the order they were defined — you can't skip attributes.</p>
</li>
<li><p><strong>No backfilling required.</strong> When you add a multi-attribute GSI to an existing table, DynamoDB automatically indexes all existing items using their natural attributes.</p>
</li>
<li><p><strong>No additional cost</strong> beyond standard GSI pricing.</p>
</li>
</ul>
<p>The model stays clean — no synthetic attributes needed:</p>
<pre><code class="language-csharp">[DynamoDBTable("Orders")]
public class OrderDto
{
    [DynamoDBHashKey("customerId")]
    public string CustomerId { get; set; }

    [DynamoDBRangeKey("createdAt")]
    public long CreatedAt { get; set; }

    [DynamoDBProperty("orderId")]
    public string OrderId { get; set; }

    [DynamoDBProperty("status")]
    public string Status { get; set; }

    [DynamoDBProperty("total")]
    public decimal Total { get; set; }
}
</code></pre>
<h3 id="heading-defining-a-multi-attribute-gsi">Defining a Multi-Attribute GSI</h3>
<p>You can create the GSI via the AWS Console (select the attributes you want in order), via Terraform (requires AWS provider v6.29.0+), or via the AWS CLI.</p>
<p>The key concept to understand: you provide <strong>multiple</strong> <code>HASH</code> <strong>entries</strong> for the composite partition key and <strong>multiple</strong> <code>RANGE</code> <strong>entries</strong> for the composite sort key, in the exact order they should be evaluated. DynamoDB treats them internally as one composite partition key and one composite sort key.</p>
<p>Here's an AWS CLI example — a GSI on Orders with a composite partition key (<code>customerId</code> + <code>status</code>) and a single-attribute sort key (<code>createdAt</code>):</p>
<pre><code class="language-bash">aws dynamodb update-table \
  --table-name Orders \
  --attribute-definitions \
    AttributeName=customerId,AttributeType=S \
    AttributeName=status,AttributeType=S \
    AttributeName=createdAt,AttributeType=N \
  --global-secondary-index-updates \
  "[{\"Create\":{
    \"IndexName\":\"customerStatus-createdAt-index\",
    \"KeySchema\":[
      {\"AttributeName\":\"customerId\",\"KeyType\":\"HASH\"},
      {\"AttributeName\":\"status\",\"KeyType\":\"HASH\"},
      {\"AttributeName\":\"createdAt\",\"KeyType\":\"RANGE\"}
    ],
    \"Projection\":{\"ProjectionType\":\"ALL\"}
  }}]"
</code></pre>
<p>The two <code>HASH</code> entries here are valid. They define a composite partition key of <code>(customerId, status)</code>. This is the syntax AWS introduced specifically for multi-attribute GSIs. It would have been rejected before November 2025.</p>
<p>Here's a Terraform example (AWS provider v6.29.0+):</p>
<pre><code class="language-hcl">global_secondary_index {
  name            = "customerStatus-createdAt-index"
  projection_type = "ALL"

  key_schema {
    attribute_name = "customerId"
    key_type       = "HASH"
  }

  key_schema {
    attribute_name = "status"
    key_type       = "HASH"
  }

  key_schema {
    attribute_name = "createdAt"
    key_type       = "RANGE"
  }
}
</code></pre>
<h3 id="heading-query-rules-you-must-follow">Query Rules You Must Follow</h3>
<p>The flexibility gain with multi-attribute GSIs is real, but the query constraints are not the same as SQL. Two rules matter most:</p>
<h4 id="heading-1-partition-key-attributes-must-all-be-supplied-with-equality-only">1. Partition key attributes must all be supplied, with equality only.</h4>
<p>For a partition key of <code>(customerId, status)</code>:</p>
<p><strong>Valid</strong>:</p>
<pre><code class="language-plaintext">customerId = 'C123' AND status = 'PENDING'
</code></pre>
<p><strong>Invalid</strong> — missing <code>status</code>:</p>
<pre><code class="language-plaintext">customerId = 'C123'
</code></pre>
<p><strong>Invalid</strong> — inequality on a partition key attribute:</p>
<pre><code class="language-plaintext">customerId = 'C123' AND status &gt; 'P'
</code></pre>
<h4 id="heading-2-sort-key-attributes-must-be-queried-left-to-right-with-inequality-only-as-the-final-condition">2. Sort key attributes must be queried left-to-right, with inequality only as the final condition.</h4>
<p>For a sort key of <code>(tournamentRound, rank, matchId)</code>:</p>
<p><strong>Valid</strong>:</p>
<pre><code class="language-plaintext">tournamentRound = 'SEMIFINALS'

tournamentRound = 'SEMIFINALS' AND rank = 'UPPER'

tournamentRound = 'SEMIFINALS' AND rank = 'UPPER' AND matchId = 'match-002'

tournamentRound = 'SEMIFINALS' AND rank = 'UPPER' AND matchId &gt; 'match-001'

tournamentRound BETWEEN 'QUARTERFINALS' AND 'SEMIFINALS'
</code></pre>
<p><strong>Invalid</strong> — skipping the first attribute:</p>
<pre><code class="language-plaintext">rank = 'UPPER'
</code></pre>
<p><strong>Invalid</strong> — leaving a gap (skipping <code>bracket</code>)</p>
<pre><code class="language-plaintext">tournamentRound = 'SEMIFINALS' AND matchId = 'match-002'
</code></pre>
<p><strong>Invalid</strong> — adding a condition after an inequality:</p>
<pre><code class="language-plaintext">tournamentRound &gt; 'QUARTERFINALS' AND rank = 'UPPER'
</code></pre>
<p><strong>Design tip:</strong> Order your sort key attributes from most general to most specific for example, <code>tournamentRound → rank → matchId</code>). This maximises query flexibility, since each left-to-right prefix becomes a valid query pattern.</p>
<h4 id="heading-going-deeper">Going deeper:</h4>
<p>AWS publishes a detailed design pattern guide with worked examples for time-series data, e-commerce orders, hierarchical organisation data, and multi-tenant SaaS platforms. The examples use the JavaScript SDK, but the schema design principles apply regardless of language. The article can be found <a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.DesignPattern.MultiAttributeKeys.html">here</a>.</p>
<h2 id="heading-c-sdk-options">C# SDK Options</h2>
<p>When working with DynamoDB in C#, the <code>AWSSDK.DynamoDBv2</code> NuGet package gives you three different ways to interact with your tables, each with different levels of abstraction.</p>
<h3 id="heading-low-level-client">Low-Level Client</h3>
<pre><code class="language-csharp">var client = new AmazonDynamoDBClient();
</code></pre>
<p>The <code>AmazonDynamoDBClient</code> gives you full control over every aspect of your DynamoDB interactions. You construct requests manually, specifying every attribute, condition, and configuration explicitly.</p>
<pre><code class="language-csharp">var request = new QueryRequest
{
    TableName = "Orders",
    KeyConditionExpression = "customerId = :customerId",
    ExpressionAttributeValues = new Dictionary&lt;string, AttributeValue&gt;
    {
        { ":customerId", new AttributeValue { S = "customer-123" } }
    }
};

var response = await client.QueryAsync(request);
</code></pre>
<p>This is the most verbose approach, but nothing is hidden from you. You can see exactly what's being sent to DynamoDB, which makes it easier to debug, optimise, and understand exactly what Read Capacity Units (RCUs) you're consuming. It's also the most flexible — anything DynamoDB supports, you can do here.</p>
<p><strong>When to use it:</strong><br>When you need fine-grained control, are doing something complex, or want full visibility into your queries.</p>
<h3 id="heading-document-model">Document Model</h3>
<pre><code class="language-csharp">var client = new AmazonDynamoDBClient();
var table = Table.LoadTable(client, "Orders");
</code></pre>
<p>The Document Model sits a level above the low-level client. Rather than working with raw <code>AttributeValue</code> types, you work with <code>Document</code> objects which feel more like JSON — familiar to most .NET developers.</p>
<pre><code class="language-csharp">var filter = new QueryFilter("customerId", QueryOperator.Equal, "customer-123");
var search = table.Query(filter);
var documents = await search.GetRemainingAsync();

// access the data
foreach (var doc in documents)
{
    Console.WriteLine(doc["orderId"].AsString());
    Console.WriteLine(doc["total"].AsDecimal());
}
</code></pre>
<p>Less boilerplate than the low-level client, but you're still working with loosely typed <code>Document</code> objects rather than your own C# classes. There's no mapping to strongly typed models out of the box.</p>
<p><strong>When to use it:</strong><br>Useful for dynamic or loosely structured data where you don't want to define a fixed model, or for quick tooling and scripts.</p>
<h3 id="heading-object-persistence-model">Object Persistence Model</h3>
<p>The Object Persistence Model is the highest level of abstraction and the most natural fit for typical .NET development.</p>
<p>You decorate your C# classes with attributes, and the <code>DynamoDBContext</code> handles serialisation and deserialisation automatically — similar to an ORM like Entity Framework.</p>
<pre><code class="language-csharp">[DynamoDBTable("Orders")]
public class OrderRecord
{
    [DynamoDBHashKey("customerId")]
    public string CustomerId { get; set; }

    [DynamoDBRangeKey("createdAt")]
    public long CreatedAt { get; set; }

    [DynamoDBProperty("orderId")]
    public string OrderId { get; set; }

    [DynamoDBProperty("total")]
    public decimal Total { get; set; }

    [DynamoDBProperty("status")]
    public string Status { get; set; }
}
</code></pre>
<p>Querying feels clean and strongly typed:</p>
<pre><code class="language-csharp">var orders = await dbContext
    .QueryAsync&lt;OrderRecord&gt;("customer-123")
    .GetRemainingAsync();
</code></pre>
<p>The trade-off is that the abstraction hides some important details: you don't always see exactly what's being sent to DynamoDB under the hood, which can make debugging and performance optimisation harder.</p>
<h3 id="heading-setting-up-the-context">Setting Up the Context</h3>
<p>When creating the db context there are a couple of options:</p>
<h4 id="heading-option-1-using-dependency-injection">Option 1 — using Dependency Injection:</h4>
<pre><code class="language-csharp">// Program.cs
builder.Services.AddSingleton&lt;IAmazonDynamoDB, AmazonDynamoDBClient&gt;();

builder.Services.AddSingleton&lt;IDynamoDBContext&gt;(sp =&gt;
{
    var client = sp.GetRequiredService&lt;IAmazonDynamoDB&gt;();
    return new DynamoDBContext(client);
});

// Then in repository / service, inject IDynamoDBContext
public class OrderRepository
{
    private readonly IDynamoDBContext _context;

    public OrderRepository(IDynamoDBContext context)
    {
        _context = context;
    }
}
</code></pre>
<h4 id="heading-option-2-register-amazondynamodbclient-only-and-instantiate-the-context-per-operation">Option 2 — register <code>AmazonDynamoDBClient</code> only, and instantiate the context per operation:</h4>
<pre><code class="language-csharp">// Program.cs
builder.Services.AddSingleton&lt;IAmazonDynamoDB, AmazonDynamoDBClient&gt;();
</code></pre>
<p>Then:</p>
<pre><code class="language-csharp">public class OrderRepository
{
    private readonly IAmazonDynamoDB _client;

    public OrderRepository(IAmazonDynamoDB client)
    {
        _client = client;
    }

    public async Task&lt;List&lt;OrderDto&gt;&gt; GetOrdersAsync(string customerId)
    {
        var context = new DynamoDBContext(_client); // lightweight to instantiate
        return await context.QueryAsync&lt;OrderDto&gt;(customerId).GetRemainingAsync();
    }
}
</code></pre>
<p><strong>Which is better?</strong> Option 1 is cleaner and more testable — you can mock <code>IDynamoDBContext</code> in unit tests easily. Option 2 is also valid since <code>DynamoDBContext</code> is lightweight to instantiate, but you lose the ability to mock it cleanly.</p>
<p><strong>When to use Object Persistence:</strong> the recommended approach for most .NET applications. Clean, strongly typed, and fits naturally into existing C# codebases.</p>
<h2 id="heading-querying-a-multi-attribute-gsi-from-c">Querying a Multi-Attribute GSI From C#</h2>
<p>At the time of writing, the <code>DynamoDBContext.QueryAsync&lt;T&gt;</code> convenience overloads don't support multi-attribute GSI key conditions directly — you need to use the low-level client (<code>IAmazonDynamoDB</code>) and pass a <code>KeyConditionExpression</code>. The good news is the deserialisation back to your typed model is still straightforward.</p>
<p>Here's a query against a GSI with a composite partition key of <code>(customerId, status)</code> and a sort key of <code>createdAt</code>, returning all pending orders for a customer since a given date:</p>
<pre><code class="language-csharp">public async Task&lt;List&lt;OrderDto&gt;&gt; GetOrdersByStatusSinceAsync(
    string customerId,
    string status,
    long fromDate)
{
    var request = new QueryRequest
    {
        TableName = "Orders",
        IndexName = "customerStatus-createdAt-index",
        KeyConditionExpression =
            "customerId = :customerId " +
            "AND #status = :status " +           // #status because 'status' is a reserved word
            "AND createdAt &gt; :fromDate",
        ExpressionAttributeNames = new Dictionary&lt;string, string&gt;
        {
            { "#status", "status" }
        },
        ExpressionAttributeValues = new Dictionary&lt;string, AttributeValue&gt;
        {
            { ":customerId", new AttributeValue { S = customerId } },
            { ":status",     new AttributeValue { S = status } },
            { ":fromDate",   new AttributeValue { N = fromDate.ToString() } }
        },
        ScanIndexForward = false // reverse sort key order — newest first, since sort key is a timestamp
    };

    var response = await _client.QueryAsync(request);

    // manually deserialise back to OrderDto using the DynamoDBContext
    return _context.FromDocuments&lt;OrderDto&gt;(
        response.Items.Select(Document.FromAttributeMap)
    ).ToList();
}
</code></pre>
<p>Looking at the code above, notice that:</p>
<ul>
<li><p>Both partition key attributes (<code>customerId</code> and <code>status</code>) are supplied with equality — this is required.</p>
</li>
<li><p>The sort key (<code>createdAt</code>) uses an inequality <code>&gt;</code> (greater than) as the final condition, which is allowed.</p>
</li>
<li><p>No synthetic string construction, no brittle formatting conventions, no backfilling existing records.</p>
</li>
</ul>
<p>If you're working on an existing codebase that uses synthetic keys, it's worth evaluating whether migrating to multi-attribute GSIs makes sense. The backfilling problem that made migrations painful before is gone, DynamoDb multi-attribute GSIs handle it automatically.</p>
<h2 id="heading-query-vs-scan">Query vs Scan</h2>
<p>This is one of the most important concepts to understand when working with DynamoDB — and one of the most common sources of performance and cost problems.</p>
<h3 id="heading-query">Query</h3>
<p>A <code>Query</code> retrieves items using the partition key, and optionally narrows results using the sort key. DynamoDB knows exactly which partition to look in, reads only the relevant items, and returns them efficiently.</p>
<pre><code class="language-csharp">// Get all orders for a customer
var orders = await _context
    .QueryAsync&lt;OrderDto&gt;("customer-123")
    .GetRemainingAsync();
</code></pre>
<p>You can narrow further using a sort key condition — for example, all orders placed in the last 30 days:</p>
<pre><code class="language-csharp">var thirtyDaysAgo = DateTimeOffset.UtcNow.AddDays(-30).ToUnixTimeMilliseconds();

var orders = await _context.QueryAsync&lt;OrderDto&gt;(
    "customer-123",
    QueryOperator.GreaterThan,
    new List&lt;object&gt; { thirtyDaysAgo }
).GetRemainingAsync();
</code></pre>
<p>Queries are fast and cheap — you only pay RCUs for the records actually read.</p>
<h3 id="heading-scan">Scan</h3>
<p>A <code>Scan</code> reads every single item in the table, then filters the results. It doesn't use keys or indexes — it brute-forces through everything.</p>
<pre><code class="language-csharp">var conditions = new List&lt;ScanCondition&gt;
{
    new ScanCondition("status", ScanOperator.Equal, "pending")
};

var orders = await _context
    .ScanAsync&lt;OrderDto&gt;(conditions)
    .GetRemainingAsync();
</code></pre>
<p>This works — but on a table with 10 million orders, DynamoDB reads all 10 million records and then filters down to the pending ones. You pay RCUs for every single record read, not just the ones returned.</p>
<p><strong>Important:</strong> Scans should be avoided in production for large tables. They're slow, expensive, and get worse as your table grows.</p>
<p>The difference visualised:</p>
<pre><code class="language-plaintext">Query:
Table [■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■]
       └── Jump straight to partition "customer-123"
               └── Read only these items — cheap

Scan:
Table [■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■]
       └── Read every single item — expensive
               └── Then discard non-matching items
</code></pre>
<h3 id="heading-when-is-a-scan-acceptable">When Is A Scan Acceptable?</h3>
<p>Scans aren't always wrong — there are legitimate use cases:</p>
<ul>
<li><p><strong>Small tables</strong> — a lookup table with 50 items, a scan is perfectly fine</p>
</li>
<li><p><strong>One-off data migrations or admin scripts</strong> — not user-facing, run occasionally</p>
</li>
<li><p><strong>Development and debugging</strong> — scanning locally or against a small dataset</p>
</li>
</ul>
<p><strong>Rule of thumb:</strong> if it's a user-facing query on a growing table, it should be a Query, not a Scan.</p>
<p>Coming from SQL, developers often reach for a Scan because it feels like:</p>
<pre><code class="language-sql">SELECT * FROM orders WHERE status = 'pending'
</code></pre>
<p>In SQL with a good index, that's fine. In DynamoDB, without a GSI on <code>status</code>, that's a full table scan every time. The solution is to design a GSI for the access patterns you need — exactly what we covered in the GSI section earlier.</p>
<h2 id="heading-filter-expressions">Filter Expressions</h2>
<p>Both Query and Scan support an optional <code>FilterExpression</code> — a condition applied <em>after</em> DynamoDB has read the records but <em>before</em> they're returned to you. It looks superficially like a SQL <code>WHERE</code> clause, and that's exactly the trap.</p>
<pre><code class="language-csharp">var request = new QueryRequest
{
    TableName = "Orders",
    KeyConditionExpression = "customerId = :customerId",
    FilterExpression = "#status = :status",
    ExpressionAttributeNames = new Dictionary&lt;string, string&gt;
    {
        { "#status", "status" }
    },
    ExpressionAttributeValues = new Dictionary&lt;string, AttributeValue&gt;
    {
        { ":customerId", new AttributeValue { S = "customer-123" } },
        { ":status",     new AttributeValue { S = "pending" } }
    }
};
</code></pre>
<p>The critical thing to understand: <code>FilterExpression</code> does <strong>not</strong> reduce the cost of the query. DynamoDB still reads every record first, charges you RCUs for all of them, and only then discards the ones that don't match the filter.</p>
<p>For a Query, that means every record matched by the <code>KeyConditionExpression</code>. For a Scan, that means every record in the table.</p>
<p>It's a convenience for trimming the <em>response payload</em>, not a tool for efficient querying. If you find yourself reaching for <code>FilterExpression</code> to support a real access pattern, that's a signal you need a GSI instead.</p>
<h2 id="heading-paging-results-and-user-interfaces">Paging Results and User Interfaces</h2>
<p>Pagination is one of the most misunderstood aspects of DynamoDB, especially if you're coming from a SQL background.</p>
<p>In SQL you might write:</p>
<pre><code class="language-sql">SELECT * FROM orders LIMIT 10 OFFSET 20
</code></pre>
<p>DynamoDB doesn't work like this. There is no concept of <code>OFFSET</code> or page numbers. Instead, DynamoDB uses cursor-based pagination via a <code>LastEvaluatedKey</code>.</p>
<h3 id="heading-how-it-works-in-dynamodb">How It Works In DynamoDB</h3>
<p>DynamoDB returns a maximum of 1MB of data per request. If your results exceed 1MB, DynamoDB returns a <code>LastEvaluatedKey</code> — a pointer to where it stopped reading. Pass this back in to the next request to continue from that position. When no <code>LastEvaluatedKey</code> is returned, you've reached the end of the data.</p>
<h3 id="heading-how-this-works-in-the-dynamodb-c-sdk">How This Works In The DynamoDB C# SDK</h3>
<p>The SDK's <code>GetRemainingAsync()</code> method handles pagination automatically, it keeps making requests until there is no <code>LastEvaluatedKey</code> left, returning everything as a single list:</p>
<pre><code class="language-csharp">// Handles all pages automatically — but loads everything into memory
var orders = await _context
    .QueryAsync&lt;OrderDto&gt;("customer-123")
    .GetRemainingAsync();
</code></pre>
<p>This is convenient but dangerous on large datasets. If a customer has 50,000 orders, you're loading all 50,000 into memory in one go.</p>
<h3 id="heading-manual-pagination-the-right-approach-for-uis">Manual Pagination — The Right Approach For UIs</h3>
<p>For a UI with "load more" or "next/previous" navigation, control pagination manually using <code>GetNextSetAsync()</code>:</p>
<pre><code class="language-csharp">// ---- Paging Model ----
public class PagedResult&lt;T&gt;
{
    public List&lt;T&gt; Items { get; set; }
    public string? PaginationToken { get; set; }
}

// ---- Repository Method ----
public async Task&lt;PagedResult&lt;OrderDto&gt;&gt; GetOrdersPageAsync(
    string customerId,
    string? paginationToken = null)
{
    var config = new DynamoDBOperationConfig
    {
        BackwardQuery = true // reverse sort key order — newest first if sort key is a timestamp
    };

    var search = _context.QueryAsync&lt;OrderDto&gt;(customerId, config);

    if (paginationToken != null)
        search.PaginationToken = paginationToken;

    var items = await search.GetNextSetAsync(25); // fetch exactly 25 records

    return new PagedResult&lt;OrderDto&gt;
    {
        Items = items,
        PaginationToken = search.PaginationToken // null if no more pages
    };
}
</code></pre>
<p>The <code>PaginationToken</code> is the SDK's serialised representation of the <code>LastEvaluatedKey</code> — pass it directly to the client as a string and receive it back on the next request.</p>
<h3 id="heading-what-about-go-to-to-page-7-navigation">What About "go to to page 7" Navigation?</h3>
<p>This isn't possible in DynamoDB. The <code>LastEvaluatedKey</code> is a position cursor, to reach page 7 you'd have to paginate through pages 1 to 6 first to obtain the correct cursor placement.</p>
<p>For most modern UIs this isn't a problem. Infinite scroll and "load more" patterns map naturally to cursor-based pagination.</p>
<h3 id="heading-the-filterexpression-trap">The <code>FilterExpression</code> Trap</h3>
<p>We've already seen that <code>FilterExpression</code> is a poor substitute for a well-designed GSI. Pagination is where it goes from "wasteful" to actively broken.</p>
<p>DynamoDB's pagination works in two stages when a <code>FilterExpression</code> is involved:</p>
<ol>
<li><p>Read records until the 1MB limit is reached</p>
</li>
<li><p>Apply the <code>FilterExpression</code>, discarding non-matching records</p>
</li>
</ol>
<p>The <code>LastEvaluatedKey</code> is generated <strong>after step 1 — before filtering</strong>. So DynamoDB can return a <code>LastEvaluatedKey</code> implying there are more results, even if the filtered page returned only a handful of records.</p>
<p>With 1,000 orders where only 50 are "pending":</p>
<pre><code class="language-plaintext">Page 1: Read 200 records → filter applied → 3 "pending" returned + LastEvaluatedKey

Page 2: Read 200 records → filter applied → 1 "pending" returned + LastEvaluatedKey

Page 3: Read 200 records → filter applied → 0 "pending" returned + LastEvaluatedKey

...and so on until all 1,000 records are read
</code></pre>
<p><strong>Important:</strong> You pay RCUs for every record <strong>read</strong>, NOT every record <strong>returned</strong>.</p>
<p>The <code>Limit</code> parameter doesn't rescue you here. <code>GetNextSetAsync(25)</code> caps the records <em>read</em> before filtering, not the records <em>returned</em>. You can read 25, filter down to 3, and still get a <code>LastEvaluatedKey</code> back, meaning your "page size of 25" actually returns somewhere between 0 and 25 results, unpredictably.</p>
<p>The real fix isn't a smarter pagination strategy, it's removing the <code>FilterExpression</code> entirely. Design a GSI keyed on the attribute you're filtering by (here, <code>status</code>, or a multi-attribute GSI with <code>status</code> in the partition key). DynamoDB then reads only the matching records directly, <code>Limit</code> caps what you actually want, and pagination behaves predictably.</p>
<h2 id="heading-final-thoughts-amp-conclusion">Final Thoughts &amp; Conclusion</h2>
<p>DynamoDB rewards you for designing around your access patterns up front, and punishes you for pretending it's SQL. The two biggest shifts from a relational mindset are:</p>
<ol>
<li><p><strong>Queries are the schema.</strong> You model tables, keys, and GSIs around the queries you need to run. You don't normalise and figure out queries later.</p>
</li>
<li><p><strong>Keys do the work.</strong> The Query operation is fast and cheap precisely because it uses the partition key to jump straight to the right data. Scans read everything, and they get worse as your data grows.</p>
</li>
</ol>
<p>The November 2025 multi-attribute GSI release is a genuinely welcome change, and is a huge improvement to the AWS resource. It removes one of the most painful ergonomic issues with DynamoDB, synthetic key construction and backfilling, without loosening the constraints that make DynamoDB fast.</p>
<p>The query rules (all partition-key attributes supplied, sort-key attributes queried left-to-right) stay exactly the same. What you gain is cleaner, typed, natural data models and the ability to add new access patterns to existing tables without a data migration.</p>
<p>For new projects, my recommendation is to use multi-attribute GSIs by default. For existing codebases built on synthetic keys, evaluate whether a migration makes sense, the painful part of such migrations is now gone.</p>
<p>As always if you want to discuss this further, or hear about my other articles drop me a follow on <a href="https://x.com/grantdotdev">'X'</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Work With Dapper in .Net ]]>
                </title>
                <description>
                    <![CDATA[ When you're working with .NET, interacting with databases (particularly SQL databases) is inevitable. Common approaches involve using ORM (Object Relational Mapping) with tools like Entity Framework.  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-work-with-dapper-in-net/</link>
                <guid isPermaLink="false">69c46b2610e664c5da070aa0</guid>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Wed, 25 Mar 2026 23:09:26 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/66b52b176a1b17f6b28d9822/d091bd67-ef80-4cae-ad84-3dc5b79ca73b.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you're working with .NET, interacting with databases (particularly SQL databases) is inevitable. Common approaches involve using ORM (Object Relational Mapping) with tools like Entity Framework.</p>
<p>Dapper stands out as a lightweight and high-performance ORM tool with numerous advantages. But where Dapper really excels is in its speed and control. Here, we'll explore Dapper's suitability for performance-critical .NET projects with simpler database relationships utilising raw SQL queries.</p>
<p>The article guides you in building a lightweight .NET Web API for a social media application with Dapper using a Postgres database.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Latest .Net SDK and runtime (at time of writing, it's .Net 10).</p>
</li>
<li><p>Knowledge of the C# programming language and how Dependency Injection works</p>
</li>
<li><p>A running PostgreSQL instance</p>
</li>
<li><p>DB Viewer software, for example DBeaver, pgAdmin, or the PostgreSQL extension for VS Code</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-what-is-dapper">What Is Dapper?</a></p>
</li>
<li><p><a href="#heading-challenges-and-advantages-of-dapper">Challenges and Advantages of Dapper</a></p>
</li>
<li><p><a href="#heading-getting-started-installation">Getting Started – Installation</a></p>
</li>
<li><p><a href="#heading-querying-data-with-dapper">Querying Data With Dapper</a></p>
</li>
<li><p><a href="#heading-querysingle-vs-queryfirst">QuerySingle vs QueryFirst</a></p>
</li>
<li><p><a href="#heading-writing-data-with-dapper">Writing Data With Dapper</a></p>
</li>
<li><p><a href="#heading-updating-data-with-dapper">Updating Data With Dapper</a></p>
</li>
<li><p><a href="#heading-deleting-records-with-dapper">Deleting Records With Dapper</a></p>
</li>
<li><p><a href="#heading-batch-processing-with-dapper">Batch Processing With Dapper</a></p>
</li>
<li><p><a href="#heading-transactions-with-dapper">Transactions With Dapper</a></p>
</li>
<li><p><a href="#heading-multi-mapping-splits">Multi-mapping / Splits</a></p>
</li>
<li><p><a href="#heading-what-comes-next-possible-additions">What Comes Next – Possible Additions</a></p>
</li>
<li><p><a href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-what-is-dapper">What Is Dapper?</h2>
<p>Dapper is a "micro ORM", providing lightweight, high-performance data access with minimal abstraction. It relies on SQL queries (these are the queries you use when interacting with an SQL database) for example:</p>
<p><code>SELECT * FROM influencers WHERE ID = 1</code>, mapping the results to objects directly.</p>
<p>Here's how Dapper's README describes what it does:</p>
<blockquote>
<p>This [Dapper] provides a simple and efficient API for invoking SQL, with support for both synchronous and asynchronous data access, and allows both buffered and non-buffered queries. –&nbsp;Dapper github repository README</p>
</blockquote>
<p>An ORM (Object-Relational Mapper) is like a translator between a programming language and a database, allowing software to interact with databases using familiar programming objects (instead of direct SQL queries), making it easier to manage and manipulate data in applications.</p>
<p>As Dapper is a "Micro-ORM", it acts a middle-ground between direct SQL and a full fledged ORM like that of the&nbsp;Entity Framework (EF) which you may have heard of before. It has a lot of the basic features, but doesn't come with all the "bloat," making it a secure and faster database tool.</p>
<h2 id="heading-challenges-and-advantages-of-dapper">Challenges and Advantages of Dapper</h2>
<h3 id="heading-challenges-of-dapper">Challenges of Dapper</h3>
<table>
<thead>
<tr>
<th><strong>Challenge</strong></th>
<th><strong>Description</strong></th>
</tr>
</thead>
<tbody><tr>
<td>No Built-in LINQ Support</td>
<td>Dapper lacks native LINQ integration, forcing developers to write raw SQL instead of using expressive LINQ queries for data filtering and projections. This can slow development for teams accustomed to LINQ in ORMs like EF Core.</td>
</tr>
<tr>
<td>Limited Conventions</td>
<td>With fewer defaults and automations, Dapper requires explicit mappings and configurations for schemas or parameters, leading to more setup code compared to convention-heavy ORMs.</td>
</tr>
<tr>
<td>Fewer Features for Complex Data Models</td>
<td>Handling intricate entity relationships (for example, deep hierarchies or many-to-many joins) demands custom SQL and manual mapping, increasing complexity in apps with rich relational data.</td>
</tr>
<tr>
<td>No Lazy Loading</td>
<td>Related entities aren't loaded on-demand – everything must be eagerly fetched via multi-mapping or separate queries, potentially causing performance issues or extra code for deferred loading.</td>
</tr>
<tr>
<td>Manual SQL Writing</td>
<td>Developers must craft all queries by hand, which can introduce errors, SQL injection risks (if parameters are mishandled), and more boilerplate, making maintenance tougher than auto-generated queries in full ORMs.</td>
</tr>
<tr>
<td>No Change Tracking</td>
<td>Dapper doesn't monitor object changes, so updates and deletes require manual SQL, which can complicate write operations and lead to inefficiencies in CRUD-heavy apps.</td>
</tr>
<tr>
<td>Lack of Database Migrations</td>
<td>No built-in tools for schema evolution or versioning, forcing reliance on external libraries or manual scripts – a stark contrast to EF Core's integrated migrations.</td>
</tr>
<tr>
<td>Connection Management Challenges</td>
<td>While Dapper uses ADO.NET connections, improper handling (for example, not disposing properly) can lead to pooling issues or leaks, especially in high-concurrency web apps.</td>
</tr>
</tbody></table>
<h3 id="heading-advantages-of-dapper">Advantages of Dapper</h3>
<p>But let’s not dwell on the negatives. Now let's concentrate on the advantages, as these far outweigh the challenges.</p>
<table>
<thead>
<tr>
<th>Advantage</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>Minimal Abstraction</td>
<td>As a micro-ORM, Dapper offers a thin wrapper over ADO.NET, giving developers direct access to SQL without heavy abstractions. This is ideal for those who want precise control over data access code and prefer working closer to the barebones.</td>
</tr>
<tr>
<td>Optimised for Read-Heavy Workloads</td>
<td>Dapper shines in applications with frequent reads, allowing custom SQL optimisations for queries like reporting or dashboards, resulting in faster data retrieval compared to feature-rich ORMs.</td>
</tr>
<tr>
<td>High Performance</td>
<td>Known for its lightweight design and low overhead, Dapper delivers extremely fast execution, especially with large datasets or high-throughput operations. It often outperforms Entity Framework in benchmarks for raw speed.</td>
</tr>
<tr>
<td>Full Control Over SQL Queries</td>
<td>Developers write explicit SQL, enabling fine-tuned queries for complex scenarios; while this requires manual management, it avoids the "black box" issues of auto-generated SQL in other ORMs.</td>
</tr>
<tr>
<td>Flexible Result Mapping</td>
<td>Dapper allows easy mapping of query results to custom objects or view models with custom logic, supporting features like multi-mapping for joins without rigid conventions.</td>
</tr>
<tr>
<td>Simplicity and Ease of Use</td>
<td>With a small API surface and quick setup, Dapper has a low learning curve, making it accessible for rapid prototyping or integrating into existing .NET projects without bloat.</td>
</tr>
<tr>
<td>Support for Multiple Result Sets</td>
<td>Using methods like QueryMultiple, Dapper efficiently handles queries returning several result sets in one call, reducing roundtrips to the database and boosting efficiency.</td>
</tr>
<tr>
<td>Built-in SQL Injection Protection</td>
<td>Automatic parameterisation secures queries against injection attacks, combining raw SQL flexibility with safety out of the box.</td>
</tr>
<tr>
<td>Broad Database Compatibility</td>
<td>Built on ADO.NET, Dapper works seamlessly with any database provider (for example, SQL Server, PostgreSQL), offering versatility without vendor lock-in.</td>
</tr>
</tbody></table>
<h2 id="heading-getting-started-installation">Getting Started – Installation</h2>
<p>You’re going to use the CLI (Command Line Interface) to install Dapper in your project. I've chosen this method, as it’s not only quicker, but will help build confidence with the CLI.</p>
<h3 id="heading-cloning-the-repo">Cloning the Repo:</h3>
<p>In your terminal app, navigate to the folder where you want the repository to be located and run the following command to clone the public tutorial repository:</p>
<pre><code class="language-bash">git clone https://github.com/grant-dot-dev/dapper_tutorial.git
</code></pre>
<p>You then need to add Dapper and the Postgres support to your project. You can do this with the following command:</p>
<pre><code class="language-bash">cd FCC.DapperTutorial

# add Dapper nuget package
dotnet add package Dapper

# add Postgres driver
dotnet add package Npgsql
</code></pre>
<p>Update your <code>defaultConnection</code> connection string within <code>appsettings.json</code> with the location of your <code>Postgres</code> instance.</p>
<pre><code class="language-json">"ConnectionStrings": {
  "DefaultConnection": "Host=localhost;Port=5432;Database=social_media;Username=postgres;Password=yourpassword"
}
</code></pre>
<h3 id="heading-create-seeding-file">Create Seeding File</h3>
<p>Create a folder in your project called <strong>Infrastructure</strong>. Then within this folder create a file called <code>DbUtilities.cs</code> and populate it with the following code:</p>
<pre><code class="language-csharp">using Dapper;
using Npgsql;
using Microsoft.Extensions.Configuration;

namespace DapperTutorial.Infrastructure;

public static class DBUtilities
{
    private const string CreateUserSql = @"
        CREATE TABLE IF NOT EXISTS ""User"" (
            UserId TEXT PRIMARY KEY,
            Username TEXT,
            FirstName TEXT,
            LastName TEXT,
            Avatar TEXT,
            Email TEXT,
            DOB DATE
        );";

    private const string CreatePostSql = @"
        CREATE TABLE IF NOT EXISTS Post (
            PostId TEXT PRIMARY KEY,
            Likes INTEGER,
            Content TEXT,
            Timestamp TIMESTAMP,
            UserId TEXT,
            FOREIGN KEY(UserId) REFERENCES ""User""(UserId)
        );";

    private const string InsertUsersSql = @"
        INSERT INTO ""User"" (UserId, Username, FirstName, LastName, Avatar, Email, DOB) VALUES
            ('1', 'iron_man', 'Tony', 'Stark', NULL, 'tony.stark@example.com', '1970-05-29'),
            ('2', 'batman', 'Bruce', 'Wayne', NULL, 'bruce.wayne@example.com', '1972-11-11'),
            ('3', 'spiderman', 'Peter', 'Parker', NULL, 'peter.parker@example.com', '1995-08-10'),
            ('4', 'wonderwoman', 'Diana', 'Prince', NULL, 'diana.prince@example.com', '1985-04-02'),
            ('5', 'superman', 'Clark', 'Kent', NULL, 'clark.kent@example.com', '1980-07-18'),
            ('6', 'black-widow', 'Natasha', 'Romanoff', NULL, 'natasha.romanoff@example.com', '1983-06-25'),
            ('7', 'deadpool', 'Wade', 'Wilson', NULL, 'wade.wilson@example.com', '1977-02-19'),
            ('8', 'green-lantern', 'Hal', 'Jordan', NULL, 'hal.jordan@example.com', '1988-09-05'),
            ('9', 'captain-america', 'Steve', 'Rogers', NULL, 'steve.rogers@example.com', '1920-07-04'),
            ('10', 'catwoman', 'Selina', 'Kyle', NULL, 'selina.kyle@example.com', '1982-12-08')
        ON CONFLICT (UserId) DO NOTHING;";

    private const string InsertPostsSql = @"
        INSERT INTO Post (PostId, Likes, Content, Timestamp, UserId) VALUES
            ('p1', 10, 'Hello, world!', '2025-10-12 10:00:00', '1'),
            ('p2', 5, 'My first post!', '2025-10-12 11:00:00', '2'),
            ('p3', 7, 'Excited to join!', '2025-10-12 12:00:00', '3'),
            ('p4', 3, 'What a great day!', '2025-10-12 13:00:00', '4'),
            ('p5', 15, 'Superhero meetup!', '2025-10-12 14:00:00', '5')
        ON CONFLICT (PostId) DO NOTHING;";

    public static async Task SeedDatabaseAsync(IConfiguration configuration)
    {
        var connectionString = configuration.GetConnectionString("DefaultConnection");

        await using var connection = new NpgsqlConnection(connectionString);
        await connection.OpenAsync();

        await using var transaction = await connection.BeginTransactionAsync();

        try
        {
            await connection.ExecuteAsync(CreateUserSql, transaction: transaction);
            await connection.ExecuteAsync(CreatePostSql, transaction: transaction);

            var userCount = await connection.QuerySingleAsync&lt;int&gt;(
                @"SELECT COUNT(*) FROM ""User"";", transaction: transaction);

            var postCount = await connection.QuerySingleAsync&lt;int&gt;(
                "SELECT COUNT(*) FROM Post;", transaction: transaction);

            if (userCount &gt; 0 &amp;&amp; postCount &gt; 0)
            {
                await transaction.CommitAsync();
                return;
            }

            await connection.ExecuteAsync(InsertUsersSql, transaction: transaction);
            await connection.ExecuteAsync(InsertPostsSql, transaction: transaction);

            await transaction.CommitAsync();
        }
        catch (Exception)
        {
            await transaction.RollbackAsync();
            throw;
        }
    }
}
</code></pre>
<p>Add a <strong>seed</strong> endpoint to your API within the <code>Program.cs</code> file as normal:</p>
<pre><code class="language-csharp">// add the using statement
using DapperTutorial.Infrastructure;

// add the seed endpoint
app.MapPost("/seed", async (IConfiguration configuration) =&gt;
{
	try
	{
		await DBUtilities.SeedDatabaseAsync(configuration);
		return Results.Ok("Database seeded successfully.");
	}
	catch (Exception ex)
	{
		return Results.Problem($"An error occurred while seeding the database: {ex.Message}");
	}
});
</code></pre>
<p>In your terminal, run the app with <code>dotnet run</code>, call the <code>/seed</code> endpoint, and this will initiate the database. Check the records have seeded correctly by viewing your database within your preferred database tool.</p>
<h2 id="heading-querying-data-with-dapper">Querying Data With Dapper</h2>
<p>This section of the tutorial will focus on the basics of using the Dapper library to query the database. We'll explore the basics of loading, saving, and protecting your SQL database.</p>
<p>Firstly, we'll implement the <strong>repository pattern</strong>, a commonly used pattern in development. A repository acts as one stop place for all your database functions. Using a repository which implements an interface allows you to easily test and mock your database functionality.</p>
<p>Start by creating the models for your data within a Models folder.</p>
<pre><code class="language-csharp">// User.cs Model
public sealed class User
{
	public string UserId { get; init; } = string.Empty;
	public string Username { get; init; } = string.Empty;
	public string FirstName { get; init; } = string.Empty;
	public string LastName { get; init; } = string.Empty;
	public string? Avatar { get; init; }
	public string Email { get; init; } = string.Empty;
	public DateOnly? DOB { get; init; }
}

// Post.cs Model
public sealed class Post
{
	public string PostId { get; init; } = string.Empty;
	public int Likes { get; init; }
	public string Content { get; init; } = string.Empty;
	public DateTime Timestamp { get; init; }
	public string UserId { get; init; } = string.Empty;
}
</code></pre>
<p>Following best practice, create an<code>IRepository.cs</code> file within an <strong>Application</strong> folder, and paste the following code which defines your querying functions:</p>
<pre><code class="language-csharp">using DapperTutorial.Models;

namespace DapperTutorial.Application;

public interface IRepository
{
    Task&lt;IReadOnlyList&lt;User&gt;&gt; GetUsersAsync();
    Task&lt;User?&gt; GetUserByIdAsync(string userId);
    Task&lt;IReadOnlyList&lt;Post&gt;&gt; GetPostsAsync();
    Task&lt;Post?&gt; GetPostByIdAsync(string postId);
    Task&lt;IReadOnlyList&lt;Post&gt;&gt; GetPostsByUser(string userId);
}
</code></pre>
<p>You now have a base interface, which can be mocked for unit testing purposes within a wider application. This is one of the many perks of implementing this pattern.</p>
<p>Now, create the concrete implementation of the repository like so:</p>
<pre><code class="language-csharp">using Dapper;
using DapperTutorial.Application;
using DapperTutorial.Models;
using Npgsql;

namespace DapperTutorial.Infrastructure;

public class Repository(IConfiguration configuration) : IRepository
{
	private readonly string _connectionString = configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Missing connection string: ConnectionStrings:DefaultConnection");

	public async Task&lt;IReadOnlyList&lt;User&gt;&gt; GetUsersAsync()
	{
		const string sql = @"
			SELECT
				UserId,
				Username,
				FirstName,
				LastName,
				Avatar,
				Email,
				DOB
			FROM ""User""
			ORDER BY Username;";

		await using var connection = CreateConnection();
		var users = await connection.QueryAsync&lt;User&gt;(sql);
		return users.AsList();
	}

	public async Task&lt;User?&gt; GetUserByIdAsync(string userId)
	{
		const string sql = @"
			SELECT
				UserId,
				Username,
				FirstName,
				LastName,
				Avatar,
				Email,
				DOB
			FROM ""User""
			WHERE UserId = @UserId;";

		await using var connection = CreateConnection();
		return await connection.QuerySingleOrDefaultAsync&lt;User&gt;(sql, new { UserId = userId });
	}

	public async Task&lt;IReadOnlyList&lt;Post&gt;&gt; GetPostsAsync()
	{
		const string sql = @"
			SELECT
				PostId,
				Likes,
				Content,
				Timestamp,
				UserId
			FROM Post
			ORDER BY Timestamp DESC;";

		await using var connection = CreateConnection();
		var posts = await connection.QueryAsync&lt;Post&gt;(sql);
		return posts.AsList();
	}

	public async Task&lt;IReadOnlyList&lt;Post&gt;&gt; GetPostsByUser(string userId)
	{
		const string sql = @"
			SELECT
				PostId,
				Likes,
				Content,
				Timestamp,
				UserId
			FROM Post
			WHERE UserId = @UserId
			ORDER BY Timestamp DESC;";

		await using var connection = CreateConnection();
		var posts = await connection.QueryAsync&lt;Post&gt;(sql, new { UserId = userId });
		return posts.AsList();
	}

	public async Task&lt;Post?&gt; GetPostByIdAsync(string postId)
	{
		var sql = @"
			SELECT * FROM Post
			WHERE PostId = @PostId;";

		await using var connection = CreateConnection();
		return await connection.QuerySingleOrDefaultAsync&lt;Post&gt;(sql, new { PostId = postId });
	}

	private NpgsqlConnection CreateConnection() =&gt; new(_connectionString);
}
</code></pre>
<h4 id="heading-unpacking-the-repository">Unpacking the Repository:</h4>
<p>The primary constructor injects <code>IConfiguration</code>, which is automatically provided by ASP.Net Core's built-in dependency injection system. This means you don't need to instantiate it yourself – the framework handles that for you. This allows access to the connection string defined in <code>appsettings.json</code>, which Dapper needs in order to connect to the database.</p>
<p>You'll also notice the null-coalescing <code>??</code> operator. This is a defensive pattern that throws a clear, descriptive error immediately if the connection string is missing, rather than letting a cryptic <code>NullReferenceException</code> surface somewhere further down the line when trying to use the connection string's value, if not provided.</p>
<p>Each method contains a SQL string prefixed with the <code>@</code> character, making it a verbatim string literal. This allows the string to span multiple lines without needing <code>\n</code> escape characters or string concatenation, so we can format our SQL the same way we would in a query editor. This makes it much easier to read.</p>
<p><code>User</code> is a reserved word in PostgreSQL, hence the double quotes around <code>"User"</code>.</p>
<p>If you're familiar with SQL, you can easily use Dapper's query methods. This is the key strength of Dapper: it gives you the flexibility of plain SQL, with the added benefits of an ORM.</p>
<ul>
<li><p><code>QueryAsync()</code>: Use this method when your query could return zero or more results. It will always return a collection, never null, so you don't need to worry about null checks when no rows are found.</p>
</li>
<li><p><code>QuerySingleOrDefaultAsync()</code>: Use this method when you expect at most one result. This is useful for methods like <code>GetByIdAsync</code>, or fetching a user's profile. If no matching row is found, it returns the default value for that type – <code>null</code> for reference types like <code>User?</code>.</p>
</li>
</ul>
<h2 id="heading-querysingle-vs-queryfirst">QuerySingle vs QueryFirst</h2>
<p>Both <code>QuerySingleOrDefaultAsync</code> and <code>QueryFirstOrDefaultAsync</code> are used when you want to return a single result, and both return the default value for the type of <code>null</code> for reference types, when no rows are found.</p>
<p>The difference is in how they behave when <strong>more than one row is returned</strong>.</p>
<p><code>QuerySingleOrDefaultAsync</code> will throw an exception, which makes it the safer choice when querying by primary key, as if you ever get two results back, something has gone seriously wrong and you want to know about it immediately rather than silently returning the wrong record, such as incorrect duplicate.</p>
<p><code>QueryFirstOrDefaultAsync</code>, on the other hand, takes the first result and ignores the rest. This makes it the better fit for scenarios such as "<em>give me the most recent post by this user</em>", where multiple results are expected yet you just want the top one, typically driven by an <code>ORDER BY</code> in your query.</p>
<h2 id="heading-writing-data-with-dapper">Writing Data With Dapper</h2>
<p>You have successfully created methods for querying and returning data from the repository, but you're going to require methods for inserting and updating existing records.</p>
<h3 id="heading-insert-method">Insert Method</h3>
<p>Add a <code>CreateUserAsync()</code> method definition to the <code>IRepository</code> interface, and then copy the below implementation into your <code>Repository.cs</code>:</p>
<pre><code class="language-csharp">public async Task&lt;bool&gt; CreateUserAsync(User user)
{
    const string sql = @"
        INSERT INTO ""User"" (
            UserId,
            Username,
            FirstName,
            LastName,
            Avatar,
            Email,
            DOB
        ) VALUES (
            @UserId,
            @Username,
            @FirstName,
            @LastName,
            @Avatar,
            @Email,
            @DOB
        );";

    await using var connection = CreateConnection();
    var rowsAffected = await connection.ExecuteAsync(sql, user);
    return rowsAffected &gt; 0;
}
</code></pre>
<p>As before, state the SQL for inserting a record into the User table, with the parameters needed to assign the values.</p>
<p>A few things worth noting:</p>
<ul>
<li><p>Dapper maps the <code>user</code> object's properties directly to the <code>@</code> parameters by name, so you can just pass <code>user</code> rather than constructing an anonymous object.</p>
</li>
<li><p><code>ExecuteAsync()</code> returns the number of rows affected, so returning <code>rowsAffected &gt; 0</code> gives the caller a simple success/failure bool.</p>
</li>
<li><p><code>Avatar</code> and <code>DOB</code> are nullable on the model, and Dapper handles this gracefully: it will insert <code>NULL</code> into the database when those values are <code>null</code>.</p>
</li>
</ul>
<h3 id="heading-protecting-your-database-parameterisation-and-sql-injection">Protecting Your Database – Parameterisation and SQL Injection</h3>
<p>You may have noticed that throughout this tutorial, values are never inserted directly into the SQL string. Instead, we use placeholders like <code>@UserId</code> and <code>@Username</code>, with the actual values passed separately. This is called <strong>parameterisation</strong>, and it's one of the most important habits you can build as a developer.</p>
<p>To understand why, consider what happens without it. If you were to build a query using string interpolation:</p>
<pre><code class="language-csharp">// Never do this
var sql = $"SELECT * FROM \"User\" WHERE Username = '{username}'";


//A malicious user could pass the following as their username:
```
' OR '1'='1
</code></pre>
<p>Which would turn your query into:</p>
<pre><code class="language-sql">SELECT * FROM "User" WHERE Username = '' OR '1'='1'
</code></pre>
<p>Since <code>'1'='1'</code> is always true, this query returns every user in the database. With a more destructive payload, an attacker could drop tables, delete records, or extract sensitive data entirely. This is known as a <strong>SQL injection attack</strong>, and it remains one of the most common and damaging vulnerabilities in web applications.</p>
<p>Dapper protects you from this automatically. When you write:</p>
<pre><code class="language-csharp">const string sql = @"SELECT * FROM ""User"" WHERE Username = @Username";
var user = await connection.QuerySingleOrDefaultAsync&lt;User&gt;(sql, new { Username = username });
</code></pre>
<p>Dapper sends the SQL and the values to the database separately. The database receives the query structure first, compiles it, and then applies the value as pure data – it can never be interpreted as SQL. No matter what a user passes in, it will always be treated as a value, never as an instruction.</p>
<p>This is why throughout this tutorial, you will always see:</p>
<ul>
<li><p><code>@ParameterName</code> placeholders in the SQL string</p>
</li>
<li><p>Values passed as an anonymous object, a model, or <code>DynamicParameters</code></p>
</li>
</ul>
<p>But never string interpolation or concatenation inside a SQL string.</p>
<h2 id="heading-updating-data-with-dapper">Updating Data With Dapper</h2>
<p>You don't always want to create new records. Sometimes, you would rather update records instead. Below is the code required to do this. Add this method to your repository, not forgetting to add the definition to the repository interface:</p>
<pre><code class="language-csharp">public async Task&lt;bool&gt; UpdateUserAsync(User user)
	{
        const string sql = @"
        UPDATE ""User"" SET
            Username = @Username,
            FirstName = @FirstName,
            LastName = @LastName,
            Avatar = @Avatar,
            Email = @Email,
            DOB = @DOB
        WHERE UserId = @UserId;";

		await using var connection = CreateConnection();
		var affectedRows = await connection.ExecuteAsync(sql, user);
		return affectedRows &gt; 0;
	}
</code></pre>
<p>Like all other queries / actions with Dapper, an SQL command is written and passed to <code>ExecuteAsync()</code> along with the object to be updated, and Dapper will automatically map the properties based on name.</p>
<h2 id="heading-deleting-records-with-dapper">Deleting Records With Dapper</h2>
<p>Just like before, add the following code to your repository. Add your delete SQL command, and pass to the <code>ExecuteAsync()</code> method. Below, instead of passing the user object, you're manually mapping the userId to the SQL <code>`@UserId`</code> parameter.</p>
<pre><code class="language-csharp">public async Task&lt;bool&gt; DeleteUserAsync(string userId)
	{
		const string sql = @"
			DELETE FROM ""User""
			WHERE UserId = @UserId;";

		await using var connection = CreateConnection();
		var affectedRows = await connection.ExecuteAsync(sql, new { UserId = userId });
		return affectedRows &gt; 0;
	}
</code></pre>
<p>There may be a time where you don't want to delete one record at a time, and instead would like to delete multiple in one go. This can be achieved by Dapper's internal mechanics, and passing a list of items to the <code>ExecuteAsync()</code> method, like so:</p>
<pre><code class="language-csharp">public async Task&lt;bool&gt; DeleteUsersBatchAsync(IEnumerable&lt;string&gt; userIds)
{
    const string sql = @"
        DELETE FROM ""User""
        WHERE UserId = @UserId;";

    await using var connection = CreateConnection();
    var affectedRows = await connection.ExecuteAsync(sql, userIds.Select(id =&gt; new { UserId = id }));

    return affectedRows &gt; 0;
}
</code></pre>
<p>Dapper will iterate over the collection and execute the statement once per item. It's not a single batch query under the hood it's multiple executions, so for large datasets you'd want to consider a different approach – but for typical use cases it's clean and convenient.</p>
<h2 id="heading-batch-processing-with-dapper">Batch Processing With Dapper</h2>
<p>For true batch processing in a single round trip, you would need to lean on SQL rather than Dapper itself. Dapper is just the messenger here, and the batching logic lives in the query.</p>
<p>The most common approach is using <code>In / Any</code> (depending on SQL database flavour):</p>
<pre><code class="language-csharp">public async Task&lt;bool&gt; DeleteUsersAsync(IEnumerable&lt;string&gt; userIds)
{
    const string sql = @"
        DELETE FROM ""User""
        WHERE UserId = ANY(@UserIds);";

    await using var connection = CreateConnection();
    var affectedRows = await connection.ExecuteAsync(sql, new { UserIds = userIds.ToArray() });
    return affectedRows &gt; 0;
}
</code></pre>
<p>This sends a single query to the database with all the IDs, rather than one query per ID. Npgsql supports passing an array directly to <code>ANY()</code>, which is the PostgreSQL idiom for this.</p>
<p><strong>Note:</strong> <code>ANY(@UserIds)</code> is PostgreSQL-specific syntax. If you were using SQL Server you'd use <code>WHERE UserId IN @UserIds</code> instead – Dapper has built-in support for expanding <code>IN</code> parameters when passed an <code>IEnumerable</code>.</p>
<p>In summary:</p>
<ul>
<li><p><strong>Multiple executions</strong>: one round trip per item, simple but inefficient at scale, especially when deleting a lot of users, or their posts.</p>
</li>
<li><p><code>ANY</code> <strong>/</strong> <code>IN</code>: single round trip regardless of list size, much more efficient for larger datasets.</p>
</li>
</ul>
<p>For most everyday use cases, the difference won't matter. But it's a good habit to reach for the single query approach when you know you're dealing with a collection.</p>
<div>
<div>💡</div>
<div>Remember to keep the <code>IRepository</code> interface updated with any new methods you've learnt within this tutorial so far</div>
</div>

<h2 id="heading-transactions-with-dapper"><strong>Transactions With Dapper</strong></h2>
<p>If you cast your mind back to the seeding utility, you may have noticed a transaction was already being used there. Now it's time to understand what they are and why they matter.</p>
<p>A transaction guarantees that either <strong>all</strong> operations succeed, or <strong>none</strong> of them do. This is the atomicity principle: the database will never be left in a half-finished state.</p>
<p>Imagine the scenario where a new user signs up to the social media application, and you ask them to write their first post, during the registration process – meaning their account and their first post is stored in the same operation.</p>
<p>Without a transaction, if the user insert succeeds but the post insert fails, you're left with an orphaned user record in the database – an account with no post, and no way to know something went wrong.</p>
<p>If you're familiar with SQL, you may have written transactions directly like so:</p>
<pre><code class="language-sql">BEGIN TRANSACTION;
    INSERT INTO "User" (UserId, Username ...) VALUES (...);
    INSERT INTO Post (PostId, ...) VALUES (...);
COMMIT;
</code></pre>
<p>With Dapper, rather than writing the transaction logic inside the SQL itself, you manage it from your C# code instead. Each SQL statement stays as its own focused block, and you wrap them together in a transaction at the application level. This gives you the same atomicity guarantee, but with the added benefit of being able to use C#'s <code>try/catch</code> to handle failures and trigger a rollback cleanly.</p>
<pre><code class="language-csharp">public async Task&lt;bool&gt; CreateUserWithPostAsync(User user, Post post)
{
    const string insertUserSql = @"
        INSERT INTO ""User"" (UserId, Username, FirstName, LastName, Avatar, Email, DOB)
        VALUES (@UserId, @Username, @FirstName, @LastName, @Avatar, @Email, @DOB);";

    const string insertPostSql = @"
        INSERT INTO Post (PostId, Likes, Content, Timestamp, UserId)
        VALUES (@PostId, @Likes, @Content, @Timestamp, @UserId);";

    await using var connection = CreateConnection();
    await connection.OpenAsync();
    await using var transaction = await connection.BeginTransactionAsync();

    try
    {
        await connection.ExecuteAsync(insertUserSql, user, transaction);
        await connection.ExecuteAsync(insertPostSql, post, transaction);
        await transaction.CommitAsync();
        return true;
    }
    catch (Exception)
    {
        await transaction.RollbackAsync();
        return false;
    }
}
</code></pre>
<p>It also means you can write some useful methods, which run particular SQL blocks conditionally. Take the below example: there are 3 different SQL blocks which can be run as part of the transaction <code>insertUserSql</code>, <code>insertPostSql</code> and <code>logActivitySql</code>.</p>
<p>For the below example, imagine you have an <code>ActivityLog</code> table that tracks user actions across the application. All three blocks are part of the same transaction, but only two of them are guaranteed to run. If the flag is inactive, the activity log is skipped entirely, and the transaction commits with just the user and post. If anything fails, all of it rolls back regardless.</p>
<pre><code class="language-csharp">public async Task&lt;bool&gt; CreateUserWithPostAndLogAsync(User user, Post post, bool logActivity)
{
    const string insertUserSql = @"
        INSERT INTO ""User"" (UserId, Username, FirstName, LastName, Avatar, Email, DOB)
        VALUES (@UserId, @Username, @FirstName, @LastName, @Avatar, @Email, @DOB);";

    const string insertPostSql = @"
        INSERT INTO Post (PostId, Likes, Content, Timestamp, UserId)
        VALUES (@PostId, @Likes, @Content, @Timestamp, @UserId);";

    const string logActivitySql = @"
        INSERT INTO ActivityLog (UserId, Action, Timestamp)
        VALUES (@UserId, 'SIGNUP', @Timestamp);";

    await using var connection = CreateConnection();
    await connection.OpenAsync();
    await using var transaction = await connection.BeginTransactionAsync();

    try
    {
        await connection.ExecuteAsync(insertUserSql, user, transaction);
        await connection.ExecuteAsync(insertPostSql, post, transaction);

        if (logActivity)
        {
            await connection.ExecuteAsync(logActivitySql, new { user.UserId, Timestamp = DateTime.UtcNow }, transaction);
        }

        await transaction.CommitAsync();
        return true;
    }
    catch (Exception)
    {
        await transaction.RollbackAsync();
        return false;
    }
}
</code></pre>
<h2 id="heading-multi-mapping-splits"><strong>Multi-mapping / Splits</strong></h2>
<p>So far, every query has returned data from a single table, mapped to a single object. But real applications rarely work that way. Data is relational, and you'll frequently need to query across multiple tables at once using a <strong>JOIN</strong>.</p>
<p>Dapper handles this through <strong>multi-mapping</strong>, which allows a single query to map results across multiple objects simultaneously. To demonstrate this, you're going to return a list of posts with their associated author's details populated (rather than just a <code>UserId</code> foreign key reference).</p>
<p>First, create the model used to store the combined data in your Models folder.</p>
<pre><code class="language-csharp">public sealed class PostWithAuthor
{
    public Post Post { get; init; } = null!;
    public User Author { get; init; } = null!;
}
</code></pre>
<p>Now add the following method to your repository (not forgetting to always add the method definition to your interface too).</p>
<pre><code class="language-csharp">public async Task&lt;IReadOnlyList&lt;PostWithAuthor&gt;&gt; GetPostsWithAuthorsAsync()
{
    const string sql = @"
        SELECT
            p.PostId,
            p.Likes,
            p.Content,
            p.Timestamp,
            p.UserId,
            u.UserId,
            u.Username,
            u.FirstName,
            u.LastName,
            u.Avatar,
            u.Email,
            u.DOB
        FROM Post p
        INNER JOIN ""User"" u ON p.UserId = u.UserId
        ORDER BY p.Timestamp DESC;";

    await using var connection = CreateConnection();

    var results = await connection.QueryAsync&lt;Post, User, PostWithAuthor&gt;(
        sql,
        (post, user) =&gt; new PostWithAuthor { Post = post, Author = user },
        splitOn: "UserId"
    );

    return results.AsList();
}
</code></pre>
<p>There is quite a bit going on here compared to the queries you have written so far, so let's unpack it.</p>
<h3 id="heading-select-statement"><strong>SELECT Statement</strong></h3>
<p>Unlike previous queries where you could rely on <code>SELECT *</code>, column order matters here. Dapper needs to know where the <code>Post</code> columns end and the <code>User</code> columns begin in the result set. You're explicitly listing every column, with all <code>Post</code> columns first and all <code>User</code> columns second.</p>
<pre><code class="language-csharp">QueryAsync&lt;Post, User, PostWithAuthor&gt;
</code></pre>
<p>Rather than a single generic type, you are now passing three: the first two are the objects you want to map to, and the third is the return type. Dapper will split the result set into a <code>Post</code> and a <code>User</code>, and hand both to you to combine however you like.</p>
<h3 id="heading-the-mapping-function">The Mapping Function</h3>
<pre><code class="language-csharp">(post, user) =&gt; new PostWithAuthor { Post = post, Author = user }
</code></pre>
<p>This is where you stitch the two objects together. Dapper passes you the mapped <code>Post</code> and <code>User</code> for each row, and you return the combined <code>PostWithAuthor</code>. This is also where you have full control: if you wanted to flatten the result into a single object rather than a nested one, you could do that here instead.</p>
<pre><code class="language-csharp">splitOn: "UserId"
</code></pre>
<p>This is the most important part to understand. The <code>splitOn</code> parameter tells Dapper the name of the column where the first object ends and the second begins. In this case, when Dapper encounters <code>UserId</code> for the second time in the result set, it knows everything from that point onwards belongs to the <code>User</code> object.</p>
<p>It defaults to <code>Id</code> – so if your split column were named <code>Id</code>, you wouldn't need to specify it at all. Since both <code>Post</code> and <code>User</code> share <code>UserId</code> as their identifier, you must specify it explicitly here.</p>
<h4 id="heading-a-common-mistake">A Common Mistake</h4>
<p>Because <code>splitOn</code> works by column position, the column you split on must be the <strong>first column of the second object</strong> in your SELECT. If <code>u.UserId</code> were not the first <code>User</code> column listed, Dapper would split in the wrong place and you'd end up with incorrectly mapped data. It's the kind of bug that can be tricky to spot because no exception is thrown, the data is just wrong.</p>
<h2 id="heading-what-comes-next-possible-additions">What Comes Next – Possible Additions</h2>
<p>Use this tutorial as a base to build upon. The next steps are to look at how you could structure your project to make your repository tidier. For example, you could move your SQL commands into files of their own, and import them in to your repository, making your repository code slimmer.</p>
<p>To avoid this tutorial becoming too bloated, I tried to cover the key features of Dapper. Now you know the fundamentals, you could push your learning further to cover:</p>
<ul>
<li><p><code>QueryMultiple</code>: execute several SQL statements in a single round trip. This is ideal for dashboard-style endpoints.</p>
</li>
<li><p><strong>Dynamic parameters</strong>: build queries conditionally using <code>DynamicParameters</code>, which is useful for search and filter scenarios.</p>
</li>
<li><p><strong>Endpoint implementation</strong>: add API endpoints to call your repository methods, injecting the <code>IRepository</code> into your minimal endpoints.</p>
<p>For example:</p>
</li>
</ul>
<pre><code class="language-csharp">app.MapPost("/insert-user", async (IRepository repository, User newUser) =&gt;
{
	var success = await repository.InsertUserAsync(newUser);
	return success
		? Results.Ok($"User {newUser.Username} inserted successfully.")
		: Results.Problem("Failed to insert user.");
});
</code></pre>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Dapper won't be the right tool for every project. If you're working with complex entity relationships, frequent schema changes, or a team heavily invested in LINQ, Entity Framework may be the better fit. And there's nothing wrong with that.</p>
<p>But when performance matters, when you need precise control over what's hitting your database, or when you're working with a schema you don't own, Dapper's transparency is hard to beat. You always know exactly what query is being executed, because you wrote it yourself.</p>
<p>The trade-off is straightforward: Entity Framework gives you speed of development, Dapper gives you speed of execution. Understanding both makes you a more well-rounded .NET developer – and knowing when to reach for each one is a skill in itself.</p>
<p>If you already know SQL, Dapper's learning curve is remarkably shallow. There's no magic, no conventions to memorise, no generated queries to second-guess. Just SQL, mapped cleanly to your C# objects, and as this tutorial has hopefully shown, that doesn't have to mean unsafe or hard to maintain.</p>
<p>Again, thanks for reading and I hope you have found this tutorial useful. If you'd like to chat about anything in this tutorial or hear about future articles, you can follow me on <a href="https://x.com/grant-dot-dev">X/Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Implement Type Safe Unions in C# With OneOf ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever needed a method to return different types depending on the situation? Perhaps a payment processor that returns different payment types, an order that can be in various states with different data, or better, a file loader that handles mu... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-implement-type-safe-unions-in-c-with-oneof/</link>
                <guid isPermaLink="false">6973f8cd15349392f17d3760</guid>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Fri, 23 Jan 2026 22:40:13 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769193036631/70de89f6-047b-4894-9685-2c65c05b3620.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever needed a method to return different types depending on the situation? Perhaps a payment processor that returns different payment types, an order that can be in various states with different data, or better, a file loader that handles multiple formats?</p>
<p>In C#, we typically solve this with inheritance hierarchies, marker interfaces, or wrapper objects – all of which add complexity and reduce type safety. But luckily, there's a better way: discriminated unions using the OneOf library.</p>
<p>You may be familiar with union types if you’ve programmed with TypeScript before, as they’re one of the pivotal features of the language. Union types are not a concept which can be found natively within C#, but they are planned for a future release. Until then, you can use the <code>OneOf&lt;T1,T2..&gt;</code> library.</p>
<p>In this article, I'll show you how <code>OneOf</code> brings F#-like discriminated unions to C#, enabling you to write cleaner, more expressive, and type-safe code across a variety of scenarios – from polymorphic return types to state machines, even elegant error handling.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-oneof">What is OneOf?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-installing-oneof">Installing OneOf</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-core-concepts">Core Concepts</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-real-world-use-cases">Real-World Use Cases</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-other-use-cases">Other Use Cases</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-key-benefits-of-oneof">Key Benefits of OneOf</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-oneof">What is OneOf?</h2>
<p>The OneOf package offers discriminated unions for C#, allowing you to return one of several predefined types from a single method. Unlike a <code>Tuple</code>, which bundles multiple values together (A <strong>and</strong> B), OneOf represents a choice (A <strong>or</strong> B <strong>or</strong> C).</p>
<p>Think of it as a type-safe way to say: "This method returns either type A, <strong>or</strong> type B, <strong>or</strong> type C" – and the compiler enforces that you handle all possibilities.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Instead of this (returns both, whether you need them or not)</span>
<span class="hljs-keyword">public</span> (User user, Error error) GetUser(<span class="hljs-keyword">int</span> id) { ...  }

<span class="hljs-comment">// You can do this (returns one OR the other)</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> OneOf&lt;User, NotFound, DatabaseError&gt; <span class="hljs-title">GetUser</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> id</span>)</span> { ... }
</code></pre>
<h3 id="heading-why-oneof-matters">Why OneOf Matters</h3>
<ul>
<li><p><strong>Type safety</strong>: The compiler ensures you handle every possible return type</p>
</li>
<li><p><strong>Self-documenting</strong>: Method signatures clearly show all possible outcomes</p>
</li>
<li><p><strong>No inheritance required</strong>: Returns different types without forcing them into a class hierarchy</p>
</li>
<li><p><strong>Pattern matching</strong>: Uses <code>.Match()</code> to handle each case exhaustively</p>
</li>
<li><p><strong>Flexibility</strong>: Supports 2, 3, 4+ different return types as needed</p>
</li>
</ul>
<h2 id="heading-installing-oneof">Installing OneOf</h2>
<h3 id="heading-option-1-recommended">Option 1 (Recommended):</h3>
<p>Using the terminal, navigate to your project folder and run the below command:</p>
<pre><code class="lang-bash">dotnet add package OneOf
</code></pre>
<h3 id="heading-option-2">Option 2:</h3>
<p>Using your IDE (Visual Studio, Rider, or VS Code):</p>
<ol>
<li><p>Right-click your project file</p>
</li>
<li><p>Select "Manage NuGet Packages"</p>
</li>
<li><p>Search for "OneOf"</p>
</li>
<li><p>Click Install</p>
</li>
</ol>
<h2 id="heading-core-concepts-and-functionality">Core Concepts And Functionality</h2>
<p>There are multiple core concepts you’ll need to understand to get the most out of the OneOf library and understand its real benefits. These are:</p>
<h3 id="heading-union-types-one-of-many">Union Types: One of Many</h3>
<p>At its heart, OneOf represents a union type. A value that can be one of several predefined types at any given time. Think of it as a type-safe container that holds exactly one value, but that value could be any of the types you specify.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// This variable can hold a string OR an int OR a bool</span>
<span class="hljs-comment">// but only ONE at a time</span>
OneOf&lt;<span class="hljs-keyword">string</span>, <span class="hljs-keyword">int</span>, <span class="hljs-keyword">bool</span>&gt; myValue;

myValue = <span class="hljs-string">"hello"</span>;     <span class="hljs-comment">// Currently holds a string</span>
myValue = <span class="hljs-number">42</span>;          <span class="hljs-comment">// Now holds an int</span>
myValue = <span class="hljs-literal">true</span>;        <span class="hljs-comment">// Now holds a bool</span>
</code></pre>
<p>This is fundamentally different from a C# <code>Tuple</code> type, which holds multiple values simultaneously:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Tuple: Holds ALL values at once (AND) </span>
<span class="hljs-keyword">var</span> tuple = (<span class="hljs-string">"hello"</span>, <span class="hljs-number">42</span>, <span class="hljs-literal">true</span>); <span class="hljs-comment">// Has string AND int AND bool</span>

<span class="hljs-comment">// OneOf: Holds ONE value at a time (OR) </span>
OneOf&lt;<span class="hljs-keyword">string</span>, <span class="hljs-keyword">int</span>, <span class="hljs-keyword">bool</span>&gt; union = <span class="hljs-string">"hello"</span>; <span class="hljs-comment">// Has string OR int OR bool</span>
</code></pre>
<h3 id="heading-type-safety-and-exhaustive-handling">Type Safety and Exhaustive Handling</h3>
<p>OneOf isn't just convenient, it's compiler-enforced. When you work with a OneOf value, the compiler ensures that you handle every possible type within your <code>.Match()</code> method. This eliminates entire categories of bugs where you forget to handle a case.</p>
<p>For example:</p>
<pre><code class="lang-csharp">OneOf&lt;Success, Failure, Pending&gt; result = GetResult();

<span class="hljs-comment">// Compiler forces you to handle all three</span>
result.Match(
    success =&gt; HandleSuccess(success),
    failure =&gt; HandleFailure(failure),
);

<span class="hljs-comment">// Missing a case? Won't compile!</span>
</code></pre>
<p>You’ll get a compiler warning and if you hover over it in your IDE or code editor, you’ll see a hint like so:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769177846621/88d52e4b-3e8b-4e1b-8e2c-4eba36c6d24d.png" alt="Image showing intellisense hints, informing the developer that they have missed a handler function, based on 3 types specified and only 2 handlers" class="image--center mx-auto" width="1106" height="648" loading="lazy"></p>
<h3 id="heading-the-match-method">The <code>.Match()</code> Method</h3>
<p>The <code>.Match()</code> method is one of OneOf's killer features. It requires you to provide a handler function for each possible type in your union, ensuring you never forget to handle a case.</p>
<p>Think of it like a type-safe switch statement that the compiler enforces:</p>
<pre><code class="lang-csharp">OneOf&lt;CreditCardInfo,PayPalUser,CryptoAccount&gt; result = GetPaymentMethod(); <span class="hljs-comment">// MasterCard</span>

result.Match(
    creditCard =&gt; ProcessCreditCard(creditCard),
    paypal =&gt; ProcessPayPal(paypal),
    crypto =&gt; ProcessCrypto(crypto)
);
</code></pre>
<p><strong>How .Match() works:</strong></p>
<ol>
<li><p>OneOf determines which type the value currently holds</p>
</li>
<li><p>It executes the corresponding handler function for that type</p>
</li>
<li><p>It passes the actual value (with the correct type) to your handler</p>
</li>
<li><p>It returns the result from whichever handler executed</p>
</li>
</ol>
<p>The generic type ordering matters, especially in relation to the <code>.Match()</code> method and the defined handlers.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769194375840/9832db95-6d04-4cc0-9376-751cb4e50138.png" alt="code block showing order of return types, CreditCard, Paypal and CryptoWallet, combined with the .Match method to define each handler for each type." class="image--center mx-auto" width="1502" height="540" loading="lazy"></p>
<ul>
<li><p><strong>Generic typing order:</strong> If you declare <code>OneOf&lt;CreditCard, PayPal, CryptoWallet&gt;</code>, then <code>CreditCard</code> is <code>T0</code>, <code>PayPal</code> is <code>T1,</code> and <code>CryptoWallet</code> is <code>T2</code>. That order determines which handler in <code>.Match(...)</code> will be executed, not its type.</p>
</li>
<li><p><strong>Handler parameter names are arbitrary</strong>: You can name them <code>option1</code>, <code>foo</code>, or <code>creditCard</code>. The name doesn’t determine the type, position does. The compiler binds the first handler to <code>CreditCard</code>, the second to <code>PayPal</code>, and third to CryptoWallet.</p>
</li>
<li><p>Each handler receives a strongly-typed parameter corresponding to its position. When the first handler runs, its parameter is a <code>CreditCard</code> object (with full IntelliSense and compile-time checks).</p>
</li>
<li><p>For readability, prefer meaningful names (for example, <code>creditCard</code>, <code>payPal</code>, <code>crypto</code>) rather than <code>option1/2/3</code>, as this was only for demonstration purposes.</p>
</li>
</ul>
<h3 id="heading-accessing-values">Accessing Values</h3>
<p>While <code>.Match()</code> is the recommended approach, OneOf also provides direct type checking and access, albeit quite cumbersome and not as intuitive.</p>
<pre><code class="lang-csharp">OneOf&lt;<span class="hljs-keyword">string</span>, <span class="hljs-keyword">int</span>&gt; example = <span class="hljs-string">"hello"</span>;

<span class="hljs-comment">// Check which type it contains</span>
<span class="hljs-keyword">if</span> (example.IsT0)  <span class="hljs-comment">// Is it the first type (string)?</span>
{
    <span class="hljs-keyword">string</span> str = example.AsT0;  <span class="hljs-comment">// Get it as a string</span>
    Console.WriteLine(str);
}
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (example.IsT1)  <span class="hljs-comment">// Is it the second type (int)?</span>
{
    <span class="hljs-keyword">int</span> num = example.AsT1;  <span class="hljs-comment">// Get it as an int</span>
    Console.WriteLine(num);
}
</code></pre>
<p>You should avoid this approach in most cases for several reasons:</p>
<p>Firstly, you lose the compiler enforcement that makes <code>.Match()</code> so powerful. Want to add a third type later? The compiler won't remind you to handle it here, and your code could become brittle and be more prone to failure.</p>
<p>Secondly, it's verbose and cluttered. Instead of one clean <code>.Match()</code> call, you need multiple if-else blocks that make your code harder to read and maintain.</p>
<p>Thirdly, the <code>T0</code>, <code>T1</code>, <code>T2</code> naming convention is positional and confusing. Which type was <code>T0</code> again? You have to constantly refer back to the method signature to remember the order, which can become frustrating for yourself and development team.</p>
<p>Finally, it's error-prone. Nothing prevents you from forgetting to check <code>IsT2</code> when dealing with three or more types.</p>
<p>Use <code>.Match()</code> whenever possible. Only resort to <code>IsT0</code>/<code>AsT0</code> when you have a specific reason to check for just one type, and the others are irrelevant in the current code flow.</p>
<h2 id="heading-a-solution-to-exception-driven-control-flow">A Solution to Exception-Driven Control Flow</h2>
<p>Many codebases overuse exceptions for control flow, making code harder to follow and debug. When you see a method call, there's no indication in the signature whether it might throw an exception or what type of errors to expect. This leads to several issues:</p>
<h3 id="heading-hidden-control-flow">Hidden Control Flow:</h3>
<pre><code class="lang-csharp"><span class="hljs-comment">// What can go wrong here? The signature doesn't tell you.</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> User <span class="hljs-title">GetUser</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> id</span>)</span>
{
    <span class="hljs-keyword">var</span> user = _dbContext.Users.Find(id);
    <span class="hljs-keyword">if</span> (user == <span class="hljs-literal">null</span>)
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UserNotFoundException();  <span class="hljs-comment">// Hidden jump in control flow!</span>

    <span class="hljs-keyword">return</span> user;
}

<span class="hljs-comment">// Caller has no idea this can throw an exception</span>
<span class="hljs-keyword">var</span> user = _userService.GetUser(<span class="hljs-number">123</span>);  <span class="hljs-comment">// Might explode!</span>
Console.WriteLine(user.Name);
</code></pre>
<h3 id="heading-exceptions-as-expected-outcomes">Exceptions As Expected Outcomes</h3>
<p>When a user enters an invalid email or a record isn't found, these aren't truly <em>exceptional</em> circumstances –they're expected, predictable outcomes that should be part of your normal business logic. Using exceptions for these scenarios treats routine validation as a crisis.</p>
<h3 id="heading-performance-impact-in-hot-paths">Performance Impact in Hot Paths</h3>
<p>While not always significant, throwing exceptions involves stack unwinding which can be hundreds of times slower than returning a value. In tight loops or high-throughput APIs, this overhead accumulates quickly.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Which exceptions should I catch? All of them? Specific ones?</span>
<span class="hljs-keyword">try</span>
{
    <span class="hljs-keyword">var</span> user = _userService.GetUser(id);
    <span class="hljs-keyword">var</span> order = _orderService.CreateOrder(user);
    <span class="hljs-keyword">var</span> payment = _paymentService.ProcessPayment(order);
}
<span class="hljs-keyword">catch</span> (Exception ex)  <span class="hljs-comment">// Too broad? Catching things we shouldn't?</span>
{
    <span class="hljs-comment">// Which operation failed? Hard to tell.</span>
    <span class="hljs-keyword">return</span> StatusCode(<span class="hljs-number">500</span>, <span class="hljs-string">"Something went wrong"</span>);
}
</code></pre>
<h2 id="heading-oneof-provides-a-cleaner-alternative">OneOf Provides a Cleaner Alternative</h2>
<p>OneOf makes failures explicit, type-safe, and part of the method signature. When you see a method that returns <code>OneOf&lt;Success&lt;T&gt;, Failure&gt;</code>, you immediately know:</p>
<ol>
<li><p>This method can fail</p>
</li>
<li><p>You must handle both success and failure cases</p>
</li>
<li><p>The compiler will enforce this</p>
</li>
</ol>
<p>The following code shows how to implement it:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Define your result types</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Success</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params">T Value</span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Failure</span>(<span class="hljs-params">ErrorType Type, <span class="hljs-keyword">string</span>[] Messages</span>)</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">enum</span> ErrorType 
{
    Validation,
    NotFound,
    Database,
    Conflict,
}

<span class="hljs-comment">// The signature now TELLS you this can fail</span>
<span class="hljs-keyword">public</span> OneOf&lt;Success&lt;User&gt;, Failure&gt; GetUser(<span class="hljs-keyword">int</span> id)
{
    <span class="hljs-keyword">try</span>
    {
        <span class="hljs-keyword">var</span> user = _dbContext.Users.Find(id);

        <span class="hljs-keyword">if</span> (user == <span class="hljs-literal">null</span>)
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Failure(ErrorType.NotFound, <span class="hljs-keyword">new</span>[] { <span class="hljs-string">$"User <span class="hljs-subst">{id}</span> not found"</span> });

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Success&lt;User&gt;(user);
    }
    <span class="hljs-keyword">catch</span> (DbException ex)
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Failure(ErrorType.Database, <span class="hljs-keyword">new</span>[] { <span class="hljs-string">"Database error"</span>, ex.Message });
    }
}

<span class="hljs-comment">// Usage: Now the caller MUST handle both cases - compiler enforces it</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> IActionResult <span class="hljs-title">GetUserEndpoint</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> id</span>)</span>
{
    <span class="hljs-keyword">var</span> result = _userService.GetUser(id);

    <span class="hljs-keyword">return</span> result.Match(
        success =&gt; Ok(success.Value),
        failure =&gt; failure.Type <span class="hljs-keyword">switch</span>
        {
            ErrorType.NotFound =&gt; NotFound(<span class="hljs-keyword">new</span> { errors = failure.Messages }),
            ErrorType.Database =&gt; StatusCode(<span class="hljs-number">500</span>, <span class="hljs-keyword">new</span> { errors = failure.Messages }),
            ErrorType.Validation =&gt; BadRequest(<span class="hljs-keyword">new</span> { errors = failure.Messages }),
            ErrorType.Conflict =&gt; Conflict(<span class="hljs-keyword">new</span> { errors = failure.Messages }),
            _ =&gt; StatusCode(<span class="hljs-number">500</span>, <span class="hljs-keyword">new</span> { errors = failure.Messages })
        }
    );
}
</code></pre>
<p>What makes this better?</p>
<ul>
<li><p><strong>It’s self-documenting</strong>: The method signature explicitly states "this returns a User OR a Failure" – no hidden surprises.</p>
</li>
<li><p><strong>There’s compiler-enforced handling</strong>: Forget to handle the failure case? Compilation error. The compiler won't let you ignore potential failures.</p>
</li>
<li><p><strong>There’s clear intent</strong>: When you call a method returning <code>OneOf&lt;Success&lt;T&gt;, Failure&gt;</code>, you know immediately you need to handle both paths. No guessing about which exceptions might be thrown.</p>
</li>
</ul>
<h2 id="heading-when-to-still-use-exceptions"><strong>When to Still Use Exceptions:</strong></h2>
<p>The goal isn't to eliminate exceptions entirely, but to reserve them for truly exceptional circumstances while using <code>OneOf</code> for predictable, business-logic failures. You could still use exceptions in these scenarios:</p>
<ul>
<li><p>Truly unexpected failures (out-of-memory, hardware failures)</p>
</li>
<li><p>Framework/library boundaries that expect exceptions</p>
</li>
<li><p>Constructor failures (constructors can't return Result types)</p>
</li>
<li><p>Third-party code contracts</p>
</li>
</ul>
<h2 id="heading-other-oneof-use-cases">Other OneOf Use Cases</h2>
<h3 id="heading-use-case-1-polymorphic-return-types-without-inheritance">Use Case 1: Polymorphic Return Types (Without Inheritance)</h3>
<p>When you need to return different types based on logic but don't want to force inheritance:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Different payment methods - no shared base class needed</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> OneOf&lt;CreditCardPayment, PayPalPayment, CryptoPayment&gt; <span class="hljs-title">GetPaymentMethod</span>(<span class="hljs-params">PaymentRequest request</span>)</span>
{
    <span class="hljs-keyword">return</span> request.Method <span class="hljs-keyword">switch</span>
    {
        <span class="hljs-string">"card"</span> =&gt; <span class="hljs-keyword">new</span> CreditCardPayment(request.CardNumber, request.CVV),
        <span class="hljs-string">"paypal"</span> =&gt; <span class="hljs-keyword">new</span> PayPalPayment(request.Email),
        <span class="hljs-string">"crypto"</span> =&gt; <span class="hljs-keyword">new</span> CryptoPayment(request.WalletAddress),
        _ =&gt; <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentException(<span class="hljs-string">"Unknown payment method"</span>)
    };
}
<span class="hljs-comment">// Usage - compiler enforces handling all types</span>
<span class="hljs-keyword">var</span> payment = GetPaymentMethod(request);
payment.Match(
    card =&gt; ChargeCard(card),
    paypal =&gt; ProcessPayPal(paypal),
    crypto =&gt; ProcessCrypto(crypto)
);
</code></pre>
<p>Why this is better than inheritance:</p>
<ul>
<li><p>No artificial base class needed</p>
</li>
<li><p>Each payment type can have completely different properties</p>
</li>
<li><p>Clear, explicit handling of each case</p>
</li>
<li><p>Easy to add new payment types (compiler will tell you everywhere to update)</p>
</li>
</ul>
<h3 id="heading-use-case-2-state-machines-with-rich-data">Use Case 2: State Machines With Rich Data</h3>
<p>Representing different states in a workflow where each state carries different information:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Order</span>
{
    <span class="hljs-keyword">public</span> OneOf&lt;Pending, Processing, Shipped, Delivered, Cancelled&gt; Status { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Pending</span>(<span class="hljs-params">DateTime OrderedAt</span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Processing</span>(<span class="hljs-params">DateTime StartedAt, <span class="hljs-keyword">string</span> WarehouseId</span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Shipped</span>(<span class="hljs-params">DateTime ShippedAt, <span class="hljs-keyword">string</span> TrackingNumber, <span class="hljs-keyword">string</span> Carrier</span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Delivered</span>(<span class="hljs-params">DateTime DeliveredAt, <span class="hljs-keyword">string</span> SignedBy</span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Cancelled</span>(<span class="hljs-params">DateTime CancelledAt, <span class="hljs-keyword">string</span> Reason</span>)</span>;

<span class="hljs-comment">// Each state carries relevant data</span>
<span class="hljs-keyword">var</span> statusMessage = order.Status. Match(
    pending =&gt; <span class="hljs-string">$"Order placed on <span class="hljs-subst">{pending.OrderedAt:d}</span>"</span>,
    processing =&gt; <span class="hljs-string">$"Processing in warehouse <span class="hljs-subst">{processing.WarehouseId}</span>"</span>,
    shipped =&gt; <span class="hljs-string">$"Shipped via <span class="hljs-subst">{shipped.Carrier}</span>, tracking:  <span class="hljs-subst">{shipped.TrackingNumber}</span>"</span>,
    delivered =&gt; <span class="hljs-string">$"Delivered on <span class="hljs-subst">{delivered.DeliveredAt:d}</span>, signed by <span class="hljs-subst">{delivered.SignedBy}</span>"</span>,
    cancelled =&gt; <span class="hljs-string">$"Cancelled: <span class="hljs-subst">{cancelled.Reason}</span>"</span>
);
</code></pre>
<p>Why not just use an enum?</p>
<ul>
<li><p>Enums only store the state – they can't carry additional data</p>
</li>
<li><p>With OneOf, <code>Processing</code> knows which warehouse, and <code>Shipped</code> knows the tracking number offering more functionality and potential other logic to be carried out easily</p>
</li>
<li><p>Type-safe access to state-specific data</p>
</li>
<li><p>Impossible to access wrong data for a state (compiler prevents it)</p>
</li>
</ul>
<h3 id="heading-use-case-3-multi-channel-notifications">Use Case 3: Multi-Channel Notifications</h3>
<p>Sending notifications through different channels, each with different requirements:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">EmailNotification</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> To, <span class="hljs-keyword">string</span> Subject, <span class="hljs-keyword">string</span> Body</span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">SmsNotification</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> PhoneNumber, <span class="hljs-keyword">string</span> Message</span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">PushNotification</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> DeviceToken, <span class="hljs-keyword">string</span> Title, <span class="hljs-keyword">string</span> Body</span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">InAppNotification</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> UserId, <span class="hljs-keyword">string</span> Message</span>)</span>;

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">SendNotification</span>(<span class="hljs-params">
    OneOf&lt;EmailNotification, SmsNotification, PushNotification, InAppNotification&gt; notification</span>)</span>
{
    <span class="hljs-keyword">await</span> notification.Match(
        <span class="hljs-keyword">async</span> email =&gt; <span class="hljs-keyword">await</span> _emailService.SendAsync(email.To, email.Subject, email.Body),
        <span class="hljs-keyword">async</span> sms =&gt; <span class="hljs-keyword">await</span> _smsService.SendAsync(sms.PhoneNumber, sms.Message),
        <span class="hljs-keyword">async</span> push =&gt; <span class="hljs-keyword">await</span> _pushService.SendAsync(push.DeviceToken, push.Title, push.Body),
        <span class="hljs-keyword">async</span> inApp =&gt; <span class="hljs-keyword">await</span> _notificationRepo.CreateAsync(inApp.UserId, inApp.Message)
    );
}

<span class="hljs-comment">// Usage</span>
<span class="hljs-keyword">await</span> SendNotification(<span class="hljs-keyword">new</span> EmailNotification(<span class="hljs-string">"user@example.com"</span>, <span class="hljs-string">"Welcome"</span>, <span class="hljs-string">"Hello! "</span>));
<span class="hljs-keyword">await</span> SendNotification(<span class="hljs-keyword">new</span> SmsNotification(<span class="hljs-string">"+1234567890"</span>, <span class="hljs-string">"Your code is 123456"</span>));
</code></pre>
<p>Benefits:</p>
<ul>
<li><p>Could have a single, unified notification interface</p>
</li>
<li><p>Each channel has exactly the parameters it needs</p>
</li>
<li><p>No optional/nullable properties for irrelevant fields</p>
</li>
<li><p>Clear routing logic</p>
</li>
</ul>
<h3 id="heading-use-case-4-file-format-handling">Use Case 4: File Format Handling</h3>
<p>Handling different file types and data formats:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">CsvData</span>(<span class="hljs-params"><span class="hljs-keyword">string</span>[] Lines</span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">JsonData</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> Content</span>)</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">ExcelData</span>(<span class="hljs-params">IWorkbook Workbook</span>)</span>;

<span class="hljs-function"><span class="hljs-keyword">public</span> OneOf&lt;CsvData, JsonData, ExcelData&gt; <span class="hljs-title">LoadDataFile</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> path</span>)</span>
{
    <span class="hljs-keyword">var</span> extension = Path.GetExtension(path).ToLower();

    <span class="hljs-keyword">return</span> extension <span class="hljs-keyword">switch</span>
    {
        <span class="hljs-string">". csv"</span> =&gt; <span class="hljs-keyword">new</span> CsvData(File.ReadAllLines(path)),
        <span class="hljs-string">".json"</span> =&gt; <span class="hljs-keyword">new</span> JsonData(File.ReadAllText(path)),
        <span class="hljs-string">".xlsx"</span> =&gt; <span class="hljs-keyword">new</span> ExcelData(LoadExcelFile(path)),
        _ =&gt; <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UnsupportedFileFormatException(extension)
    };
}

<span class="hljs-comment">// Process different formats uniformly</span>
<span class="hljs-keyword">var</span> data = LoadDataFile(filePath);
<span class="hljs-keyword">var</span> records = data.Match(
    csv =&gt; ParseCsv(csv.Lines),
    json =&gt; ParseJson(json.Content),
    excel =&gt; ParseExcel(excel.Workbook)
);
</code></pre>
<p>This is perfect for:</p>
<ul>
<li><p>APIs that offer multiple export formats</p>
</li>
<li><p>Import wizards that accept various file types</p>
</li>
<li><p>Configuration loaders supporting multiple formats</p>
</li>
</ul>
<h2 id="heading-key-benefits-of-oneof">Key Benefits of OneOf</h2>
<p>OneOf shines when you have:</p>
<ul>
<li><p>Multiple valid return types that don't share a common base class</p>
</li>
<li><p>Different data shapes for different scenarios</p>
</li>
<li><p>Type-safe branching where you want the compiler to enforce handling all cases</p>
</li>
<li><p>Domain modeling where different states carry different information</p>
</li>
<li><p>Explicit outcomes that should be part of the method signature</p>
</li>
</ul>
<p>It's essentially a way to say "this method returns <strong>A</strong> or <strong>B</strong> or <strong>C</strong>" in a type-safe way, forcing consumers to explicitly handle each possibility. This leads to more robust, self-documenting code that's harder to misuse.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>OneOf brings the power of discriminated unions to C#, enabling more expressive and type-safe code across numerous scenarios. Whether you're modeling payment methods, order states, notification channels, or error handling, OneOf provides a clean, compiler-enforced way to handle multiple return types.</p>
<p>Start incorporating OneOf into your projects, and you'll find your code becomes more intentional, easier to maintain, and less error-prone.</p>
<p>As always, if you’ve enjoyed reading this article feel free to <a target="_blank" href="https://x.com/grantdotdev">reach out on Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Loops in C# ]]>
                </title>
                <description>
                    <![CDATA[ Writing the same code repeatedly is poor practice in C# and doesn’t follow the Don’t Repeat Yourself (DRY) principle. But, there are many times in programming where you need to repeat commands, operations, or computations multiple times — perhaps cha... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-loops-in-c/</link>
                <guid isPermaLink="false">68caa6382769a3737407de1b</guid>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Loops ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Wed, 17 Sep 2025 12:14:48 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1757595108095/f7bd2673-3da5-4f34-8541-64cc8129fe96.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Writing the same code repeatedly is poor practice in C# and doesn’t follow the Don’t Repeat Yourself (DRY) principle. But, there are many times in programming where you need to repeat commands, operations, or computations multiple times — perhaps changing one small thing each iteration.</p>
<p>This is where loops come in. In this article, you’ll learn:</p>
<ul>
<li><p>How to create your first loop</p>
</li>
<li><p>Benefits and caveats of using loops</p>
</li>
<li><p>The different types of loops in C# and how to use them</p>
</li>
<li><p>When it’s best to use each one</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-to-use-a-for-loop-in-c">How to Use a For Loop in C</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-a-foreach-loop-in-c">How to Use a ForEach Loop in C</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-a-dowhile-loop-in-c">How to Use a Do..While Loop in C</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-a-while-loop-in-c">How to Use a While Loop in C</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts-choosing-the-right-loop">Final Thoughts: Choosing the Right Loop</a></p>
</li>
</ul>
<p>Let’s get started. Open your preferred IDE or coding editor and create a new Console Application in .Net 8+.</p>
<h2 id="heading-how-to-use-a-for-loop-in-c">How to Use a For Loop in C</h2>
<p>A for loop repeats a block of code a set number of times by:</p>
<ul>
<li><p>Initialising a loop variable.</p>
</li>
<li><p>Checking a condition before each iteration.</p>
</li>
<li><p>Updating the loop variable after each iteration.</p>
</li>
</ul>
<p>You can create a <code>for</code> loop with the code below:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// number of iterations</span>
<span class="hljs-keyword">var</span> totalIterations = <span class="hljs-number">5</span>

<span class="hljs-comment">// the loop</span>
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt;= totalIterations; i++){
   Console.Write(<span class="hljs-string">$"<span class="hljs-subst">{i}</span>,"</span>);
}

<span class="hljs-comment">// Output</span>
<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>,
</code></pre>
<p><strong>Breaking it down:</strong></p>
<ul>
<li><p><code>for</code> — declares the loop</p>
</li>
<li><p><code>int i = 0;</code> — sets the starting point for the loop variable <code>i</code></p>
</li>
<li><p><code>i &lt;= totalIterations;</code> — the condition to keep looping. The code inside the loop runs only if this is true.</p>
</li>
<li><p><code>i++</code> — shorthand for “increase <code>i</code> by 1” after each iteration</p>
</li>
</ul>
<h3 id="heading-iterations-and-zero">Iterations and Zero</h3>
<p>Why does the example print six numbers when <code>totalIterations</code> is 5? C# uses zero-based indexing. Counting from 0 → 5 includes six numbers: 0,1,2,3,4,5.</p>
<p>If you want to print 1 → 5 instead, start <code>i</code> at 1:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> totalIterations = <span class="hljs-number">5</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">1</span>; i &lt;= totalIterations; i++)
{
    Console.Write(<span class="hljs-string">$"<span class="hljs-subst">{i}</span>,"</span>);
}
<span class="hljs-comment">// Output: 1,2,3,4,5</span>
</code></pre>
<p><strong>Tip:</strong><br>In general, <code>for</code> loops are used for indexing/accessing elements in collections, so it’s common practice to start your loop variable at 0 and use <code>&lt;</code> (less than) a given number or length of the collection.</p>
<h3 id="heading-reversal-of-direction">Reversal of Direction</h3>
<p>You can reverse a <code>for</code> loop by starting at the end and decrementing <code>i</code>:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">5</span>; i &gt; <span class="hljs-number">0</span>; i--)
{
    Console.Write(<span class="hljs-string">$"<span class="hljs-subst">{i}</span>,"</span>);
}
<span class="hljs-comment">// Output: 5,4,3,2,1</span>
</code></pre>
<p>The loop checks if <code>i &gt; 0</code>. After each iteration, <code>i</code> decreases by 1, printing numbers in descending order.</p>
<h3 id="heading-other-uses-of-for-loops">Other Uses of For Loops</h3>
<p>Let’s say you want to access every other item in a list. This is where the power of <code>for</code> loops come in, and we can maximise our <code>loop variable</code>, using it as an index accessor.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Address</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Name { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } = <span class="hljs-keyword">string</span>.Empty;
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> AddressLineOne { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } = <span class="hljs-keyword">string</span>.Empty;
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> HouseNumber { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } = <span class="hljs-keyword">default</span>;
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> PostCode { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } = <span class="hljs-keyword">string</span>.Empty;
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Telephone { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } = <span class="hljs-keyword">string</span>.Empty;
}

<span class="hljs-keyword">internal</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">var</span> addressBook = <span class="hljs-keyword">new</span> List&lt;Address&gt;
        {
            <span class="hljs-keyword">new</span> Address
            {
                Name = <span class="hljs-string">"Grant"</span>, AddressLineOne = <span class="hljs-string">"Developer Avenue"</span>, HouseNumber = <span class="hljs-number">1</span>, PostCode = <span class="hljs-string">"DV19 8EP"</span>,
                Telephone = <span class="hljs-string">"0102919 93020-92019"</span>
            },
            <span class="hljs-keyword">new</span> Address
            {
                Name = <span class="hljs-string">"Bill"</span>, AddressLineOne = <span class="hljs-string">"Developer Avenue"</span>, HouseNumber = <span class="hljs-number">19</span>, PostCode = <span class="hljs-string">"DV19 8EP"</span>,
                Telephone = <span class="hljs-string">"0102919 93020-92019"</span>
            },
            <span class="hljs-keyword">new</span> Address
            {
                Name = <span class="hljs-string">"Rebecca"</span>, AddressLineOne = <span class="hljs-string">"Developer Avenue"</span>, HouseNumber = <span class="hljs-number">4</span>, PostCode = <span class="hljs-string">"DV19 8EP"</span>,
                Telephone = <span class="hljs-string">"0102919 93020-92019"</span>
            },
            <span class="hljs-keyword">new</span> Address
            {
                Name = <span class="hljs-string">"Amy"</span>, AddressLineOne = <span class="hljs-string">"Rower Avenue"</span>, HouseNumber = <span class="hljs-number">1</span>, PostCode = <span class="hljs-string">"DV19 8EP"</span>,
                Telephone = <span class="hljs-string">"0102919 93020-92019"</span>
            },
            <span class="hljs-keyword">new</span> Address
            {
                Name = <span class="hljs-string">"Joe"</span>, AddressLineOne = <span class="hljs-string">"Olympic Drive"</span>, HouseNumber = <span class="hljs-number">1</span>, PostCode = <span class="hljs-string">"DV19 10E"</span>,
                Telephone = <span class="hljs-string">"0102919 93020-92019"</span>
            }
        };

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; addressBook.Count; i += <span class="hljs-number">2</span>)
        {
            Console.WriteLine(<span class="hljs-string">$"Name: <span class="hljs-subst">{addressBook[i].Name}</span>, PostCode: <span class="hljs-subst">{addressBook[i].PostCode}</span>"</span>);
        }
    }
}

<span class="hljs-comment">/* Ouput:
Name: Grant, PostCode: DV19 8EP
Name: Rebecca, PostCode: DV19 8EP
Name: Joe, PostCode: DV19 10E
*/</span>
</code></pre>
<h3 id="heading-whats-happening"><strong>What’s Happening:</strong></h3>
<ul>
<li><p><code>i</code> starts at 0 and increases by 2 (<code>i += 2</code>) each iteration</p>
</li>
<li><p><code>addressBook[i]</code> now accesses every other item</p>
</li>
<li><p>This shows the power of using <code>i</code> as an index</p>
</li>
</ul>
<p>So far we’ve seen how <code>for</code> loops give control over indexes and step sizes.</p>
<p>But sometimes you just want to loop through every item in a collection, without worrying about indexes. That’s where the foreach loop shines.</p>
<h2 id="heading-how-to-use-a-foreach-loop-in-c">How to Use a ForEach Loop in C</h2>
<p>A <code>foreach</code> loop iterates over any object that inherits from <code>IEnumerable</code> (lists, arrays, collections). It automatically accesses each item in order, so you don’t need an index.</p>
<h3 id="heading-how-to-write-a-foreach-loop">How To Write a ForEach Loop</h3>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> characters = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt;{<span class="hljs-string">"Batman"</span>, <span class="hljs-string">"CatWoman"</span>, <span class="hljs-string">"The Joker"</span>,<span class="hljs-string">"Harley Quinn"</span>};

<span class="hljs-keyword">foreach</span>(<span class="hljs-keyword">var</span> character <span class="hljs-keyword">in</span> characters){
    Console.WriteLine(character);
}

<span class="hljs-comment">/* Output:
Batman
CatWoman
The Joker
Harley Quinn
*/</span>
</code></pre>
<p><strong>Key points:</strong></p>
<ul>
<li><p>No need for indexes</p>
</li>
<li><p>Works with any enumerable collection</p>
</li>
<li><p>Cleaner, more expressive code</p>
</li>
</ul>
<h3 id="heading-caveats-of-foreach-loop">Caveats of ForEach Loop</h3>
<h4 id="heading-no-indexing">No Indexing</h4>
<p>You can’t directly access items with <code>addressBook[i]</code> inside a foreach. That’s because <code>foreach</code> works on <code>IEnumerable</code>, which doesn’t expose indexes.</p>
<h4 id="heading-performance">Performance</h4>
<p>A <code>foreach</code> loop has a small overhead compared to a <code>for</code> loop. In most cases this won’t matter, but in performance-critical code, a <code>for</code> loop may be faster.</p>
<h4 id="heading-modifying-items">Modifying Items</h4>
<p><code>foreach</code> gives you a copy of the current item, not a direct reference. That means you can’t reassign values to the list items inside the loop.</p>
<ul>
<li><p>You can read properties.</p>
</li>
<li><p>You can’t update the items themselves (use a for loop for that).</p>
</li>
</ul>
<p><code>foreach</code> is ideal when you want to visit every item, but not control the number of iterations.</p>
<h2 id="heading-how-to-use-a-dowhile-loop-in-c">How to Use a Do..While Loop in C</h2>
<p><code>do..while</code> loops run code at least once, then repeat while a condition is true:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">int</span> num;
<span class="hljs-keyword">do</span> {
    Console.Write(<span class="hljs-string">"Enter a positive number: "</span>);
    num = <span class="hljs-keyword">int</span>.Parse(Console.ReadLine());
} <span class="hljs-keyword">while</span> (num &lt;= <span class="hljs-number">0</span>);

Console.WriteLine(num);
</code></pre>
<p>The above code requests a number to be entered within the console application if the condition is met. That is, if a positive number is provided, the code will not ask for another, exiting the loop.</p>
<p>Should the user enter a negative number, it would continue to loop, requesting a positive number to be entered.</p>
<p>What if you didn’t want the code to run at least once, and only if a condition is met? This where you can use a <code>while</code> loop.</p>
<h2 id="heading-how-to-use-a-while-loop-in-c">How to Use a While Loop in C</h2>
<p><code>while</code> loops repeat code as long as a condition is true, but the body may never run if the condition is initially false:</p>
<p>We can use an example of a darts score board:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> sum = <span class="hljs-number">0</span>;
<span class="hljs-keyword">var</span> dartsThrown = <span class="hljs-number">0</span>;
<span class="hljs-keyword">var</span> random = <span class="hljs-keyword">new</span> Random();

<span class="hljs-keyword">while</span> (sum &lt; <span class="hljs-number">180</span> &amp;&amp; dartsThrown &lt; <span class="hljs-number">3</span>)
{
    <span class="hljs-keyword">var</span> dartScore = random.Next(<span class="hljs-number">61</span>); <span class="hljs-comment">// 0–60</span>
    sum += dartScore;
    dartsThrown++;
}

Console.WriteLine(<span class="hljs-string">"Your score is "</span> + sum);
</code></pre>
<p>While the player has darts to throw, the code will pick a number at random and increase their score.</p>
<p><strong>Tip:</strong> Always include code that changes the condition, or you risk creating an infinite loop. An infinite loop, is a loop which never stops, and causes your application to break.</p>
<p>You’ve seen four different ways to repeat code in C#: <code>for</code>, <code>foreach</code>, <code>do..while</code>, and <code>while</code>. Let’s summarise when to use each one.</p>
<h2 id="heading-final-thoughts-choosing-the-right-loop">Final Thoughts: Choosing the Right Loop</h2>
<p>C# gives us several types of loops. Choosing the right one makes your code readable, efficient, and intentional.</p>
<ul>
<li><p><strong>For Loop:</strong> Use when you know how many times to run something, or when you need an index, like using arrays, skipping items. Use a <code>for</code> loop when you need more control over the iterative nature of the loop.</p>
</li>
<li><p><strong>ForEach Loop:</strong> Use when you want to iterate through every item in a collection without worrying about indexes.</p>
</li>
<li><p><strong>While Loop:</strong> Use when you don’t know in advance how many times to run the code, but a condition drives the loop.</p>
</li>
<li><p><strong>Do..While Loop:</strong> Use when the loop body must run at least once, such as for user input or retry logic.</p>
</li>
</ul>
<p>By matching the loop type to your intent, your code will be correct, readable, and maintainable.</p>
<p>I hope this tutorial was useful, and as always I’d love to hear your thoughts and discuss further on social media. You can find me on <a target="_blank" href="https://x.com/grantdotdev">twitter/x</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Micro Frontends in React with Vite and Module Federation ]]>
                </title>
                <description>
                    <![CDATA[ Micro Frontend Architecture has become increasingly popular in recent years, as teams look to re-use parts of their existing applications in new projects rather than rebuilding everything from scratch. Micro frontends also allow large teams to share ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-micro-frontends-in-react-with-vite-and-module-federation/</link>
                <guid isPermaLink="false">68ae1d943a7c9745e83e7797</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ module federation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Microfrontend ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Tue, 26 Aug 2025 20:48:20 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1756231755011/283b9e67-9a09-4241-9d90-701cb075084d.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Micro Frontend Architecture has become increasingly popular in recent years, as teams look to re-use parts of their existing applications in new projects rather than rebuilding everything from scratch.</p>
<p>Micro frontends also allow large teams to share common components – such as headers, footers, and logins – across multiple applications, ensuring consistency and keeping their projects DRY (Don’t Repeat Yourself).</p>
<p>In this article, you will learn:</p>
<ul>
<li><p>How to set up a project and folder structure that implements a Micro Frontend (MFE) Infrastructure</p>
</li>
<li><p>How to use and configure the <code>@originjs/vite-plugin-federation</code> to allow Module Federation (MF) usage with Vite projects.</p>
</li>
<li><p>How to share and consume React components between apps.</p>
</li>
<li><p>How to run and test your app with shared components locally</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-pre-requisites">Pre-Requisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-module-federation">What Is Module Federation?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-benefits-of-module-federation">Benefits of Module Federation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-project-structure">Project Structure</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-the-projects">How to Create the Projects</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-install-dependencies">How to Install Dependencies</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-configure-the-remote-app">How to Configure the Remote App</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-vite-module-federation-plugin">The Vite Module Federation Plugin</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-key-constraints-on-exported-modules">Key Constraints On Exported Modules</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-the-remote-components">How to Create the Remote Components</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-consume-the-remote-components-within-the-host">How to Consume the Remote Components Within the Host</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-handle-typescript-errors">How to Handle TypeScript Errors</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-remotewrappercomponent-to-apptsx">How to Add RemoteWrapperComponent to App.tsx</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-serve-the-remote-app-and-run-your-host">How to Serve the Remote App and Run Your Host</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-true-power-of-micro-frontends">The True Power of Micro Frontends</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-pre-requisites">Pre-Requisites</h2>
<ul>
<li><p>Node installed on your machine – you can get your download of Node <a target="_blank" href="https://nodejs.org/en/download">here</a></p>
</li>
<li><p>Familiarity with JS / TS and React</p>
</li>
<li><p>Familiarity with Command Line / Terminal</p>
</li>
</ul>
<h2 id="heading-what-is-module-federation">What Is Module Federation?</h2>
<p>Before we go any further, let’s talk about Module Federation (MF).</p>
<p>Module Federation is a technique in web development that allows multiple separate builds to work together as a single application. It enables the sharing of code between different, independent applications at runtime, rather than at build time. This means a host application can dynamically load and execute code from a remote application.</p>
<p>At its core, Module Federation uses a <strong>"host"</strong> and <strong>"remote"</strong> architecture. A <strong>host</strong> application is the main app that consumes shared code / components. A <strong>remote</strong> application is the one that exposes code to be consumed.</p>
<p>The remote app specifies <strong>which</strong> parts of its code, or "modules," are available for others to use. The host then references these modules and loads them as needed.</p>
<h2 id="heading-benefits-of-module-federation">Benefits of Module Federation</h2>
<h3 id="heading-independent-deployment">Independent Deployment</h3>
<p>Module Federation lets teams build and deploy their micro-frontends separately. This eliminates the need for full application redeployments, accelerating development and reducing risk.</p>
<h3 id="heading-efficient-code-sharing">Efficient Code Sharing</h3>
<p>MF provides a native way to share code and dependencies between micro-frontends. This prevents code duplication, reducing bloat and ensuring consistency.</p>
<h3 id="heading-performance-gains">Performance Gains</h3>
<p>By sharing dependencies, Module Federation reduces the overall bundle size and improves initial load times, as each micro-frontend doesn't download its own copy of common libraries.</p>
<h3 id="heading-scalability-and-maintainability">Scalability and Maintainability</h3>
<p>MF enables a scalable architecture by breaking down large applications into smaller, manageable micro-frontends. This makes the codebase easier to maintain and allows teams to work independently.</p>
<h3 id="heading-analogy">Analogy</h3>
<p>Imagine building an online store. Traditionally, you’d create the entire site – homepage, product pages, cart, user profile – as one big application. A small cart change would require rebuilding and redeploying everything.</p>
<p>With Micro Frontends and Module Federation, the shopping cart can be its own app, built and maintained by a dedicated team. The main site simply imports it, allowing the cart team to release updates independently, speeding up development and improving focus.</p>
<p>This also works for organisations with multiple sites that need a consistent look. Shared components like a header, footer, or product card can be reused across sites with different purposes, such as hiring vehicles or selling furniture, ensuring visual consistency while keeping functionality unique.</p>
<h2 id="heading-project-structure">Project Structure</h2>
<p>You will need to create two projects:</p>
<ul>
<li><p><strong>host</strong> – this will act as your host application</p>
</li>
<li><p><strong>remote</strong> – this will expose the components you want to share</p>
</li>
</ul>
<h2 id="heading-how-to-create-the-projects">How to Create the Projects</h2>
<p>Run the following commands in your terminal to create your root project folder and your two Vite projects:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># create micro-frontends directory for two vite projects, and navigate to folder</span>
mkdir micro-frontends; <span class="hljs-built_in">cd</span> micro-frontends
</code></pre>
<h3 id="heading-create-a-git-repository-optional">Create a Git Repository (Optional)</h3>
<p>Using the below command you can create a Git repository for source control.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># initiate git repo</span>
git init
</code></pre>
<pre><code class="lang-bash"><span class="hljs-comment"># create host application vite app</span>
npm create vite@latest host-app

<span class="hljs-comment"># once submitted the command, select React and press Enter, </span>
Select a framework:
│  ○ Vanilla
│  ○ Vue
│  ● React
│  ○ Preact
│  ○ Lit
│  ○ Svelte
│  ○ Solid
│  ○ Qwik
│  ○ Angular
│  ○ Marko
│  ○ Others

<span class="hljs-comment"># select Typescript + SWC and again press Enter</span>
Select a variant:
│  ○ TypeScript
│  ● TypeScript + SWC
│  ○ JavaScript
│  ○ JavaScript + SWC
│  ○ React Router v7 
│  ○ TanStack Router
│  ○ RedwoodSDK 
│  ○ RSC
</code></pre>
<p>Once this is done, navigate back to the root project folder (<code>micro-frontends</code>):</p>
<pre><code class="lang-bash"><span class="hljs-comment"># navigate back</span>
<span class="hljs-built_in">cd</span> ../

<span class="hljs-comment"># create remote-app </span>
npm create vite@latest remote-app

<span class="hljs-comment"># follow instructions as before to select React, Typescript + SWC</span>
</code></pre>
<p>You now have your two projects, <code>host-app</code> and <code>remote-app</code>.</p>
<h2 id="heading-how-to-install-dependencies">How to Install Dependencies</h2>
<p>Open the <code>micro-frontends</code> folder in your preferred IDE / Code Editor. In this tutorial I’ll be using VS Code.</p>
<p>Tip: You can open the current folder in VS Code via your terminal using the following command <code>code .</code> if you’ve already added <code>code</code> to you PATH.</p>
<p>Once you’ve opened VS Code, open the terminal and run the following command:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> host-app &amp;&amp; npm install -D @originjs/vite-plugin-federation
</code></pre>
<p>and then run:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> ../remote-app &amp;&amp; npm install -D @originjs/vite-plugin-federation
</code></pre>
<h3 id="heading-styling">Styling</h3>
<p>To see styling like my examples, you need to add Tailwind CSS to both your remote and host applications. You can find instructions on how to do so <a target="_blank" href="https://tailwindcss.com/docs/installation/using-vite">here</a></p>
<h2 id="heading-how-to-configure-the-remote-app">How to Configure the Remote App</h2>
<p>In order to be able to utilise modules and components from your remote applications, you need to configure your application to expose those modules, and your host app to consume them.</p>
<p>Use the following configurations in your apps to allow your components to be exposed and consumed – don’t worry about the components just yet, you’ll create them soon.</p>
<h3 id="heading-host-app-viteconfigts">Host App – <code>Vite.config.ts</code></h3>
<p>In the <code>host</code> app, open <code>vite.config.js</code> and add the following configuration:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">//host - vote.config.js</span>
<span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">"vite"</span>;
<span class="hljs-keyword">import</span> react <span class="hljs-keyword">from</span> <span class="hljs-string">"@vitejs/plugin-react-swc"</span>;
<span class="hljs-keyword">import</span> federation <span class="hljs-keyword">from</span> <span class="hljs-string">"@originjs/vite-plugin-federation"</span>;


<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  plugins: [
    react(),
    federation({
      name: <span class="hljs-string">"host_app"</span>,
      remotes: {
        remote_app: <span class="hljs-string">"http://localhost:5001/assets/remoteEntry.js"</span>,
      },
      shared: [<span class="hljs-string">"react"</span>, <span class="hljs-string">"react-dom"</span>],
    }),
  ],
  build: {
    modulePreload: <span class="hljs-literal">false</span>,
    target: <span class="hljs-string">"esnext"</span>,
    minify: <span class="hljs-literal">false</span>,
    cssCodeSplit: <span class="hljs-literal">false</span>,
  },
});
</code></pre>
<h3 id="heading-remote-app-viteconfigts">Remote App – <code>Vite.config.ts</code></h3>
<p>In the <code>remote</code> app, open <code>vite.config.js</code> and add the following configuration:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// remote - vite.config.js</span>
<span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">"vite"</span>;
<span class="hljs-keyword">import</span> react <span class="hljs-keyword">from</span> <span class="hljs-string">"@vitejs/plugin-react-swc"</span>;
<span class="hljs-keyword">import</span> federation <span class="hljs-keyword">from</span> <span class="hljs-string">"@originjs/vite-plugin-federation"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  plugins: [
    react(),
    federation({
      name: <span class="hljs-string">"remote_app"</span>,
      filename: <span class="hljs-string">"remoteEntry.js"</span>,
      exposes: {
        <span class="hljs-string">"./Button"</span>: <span class="hljs-string">"./src/components/Button"</span>,
        <span class="hljs-string">"./Header"</span>: <span class="hljs-string">"./src/components/Header"</span>,
      },
      shared: [<span class="hljs-string">"react"</span>, <span class="hljs-string">"react-dom"</span>],
    }),
  ],
  build: {
    modulePreload: <span class="hljs-literal">false</span>,
    target: <span class="hljs-string">"esnext"</span>,
    minify: <span class="hljs-literal">false</span>,
    cssCodeSplit: <span class="hljs-literal">false</span>,
  },
  preview: {
    port: <span class="hljs-number">5001</span>,
    strictPort: <span class="hljs-literal">true</span>,
    cors: <span class="hljs-literal">true</span>,
  },
});
</code></pre>
<h4 id="heading-explanation-of-vite-configuration">Explanation of Vite Configuration:</h4>
<p>1. <code>plugins</code>: array where you tell Vite which <strong>plugins</strong> to use when it:</p>
<ul>
<li><p>Runs your dev server</p>
</li>
<li><p>Builds your production bundle</p>
</li>
</ul>
<p>Each plugin is basically a little (or big) piece of code that hooks into Vite’s build pipeline to add extra features – for example:</p>
<ul>
<li><p>Adding React JSX/TSX support (<code>@vitejs/plugin-react</code>)</p>
</li>
<li><p>Enabling Module Federation (<code>@originjs/vite-plugin-federation</code>)</p>
</li>
</ul>
<p>2. <code>build</code>: Controls how Vite produces the production build. You don’t need to worry too much about this for the purpose of this tutorial.</p>
<p>3. <code>preview</code>: Controls how the application is served / previewed:</p>
<ul>
<li><p>Useful in microfrontend setups where a fixed port and CORS enabled are needed so other apps can fetch your remote modules.</p>
</li>
<li><p><code>strictPort: true</code> ensures predictable networking – avoids “it works on my machine” issues with random ports.</p>
</li>
</ul>
<h2 id="heading-the-vite-module-federation-plugin">The Vite Module Federation Plugin</h2>
<p>You need to configure your Vite Module Federation plugin to inform it what components are to be consumed and exposed. Let’s take a look at the configured properties:</p>
<ul>
<li><p><code>name</code>: The unique identifier for your remote application in a Module Federation setup. This is the name other applications (hosts) will use when they declare your app as a remote.</p>
</li>
<li><p><code>filename</code>: This is the name of the file that your host will load when it tries to retrieve your exposed modules.</p>
</li>
<li><p><code>exposes</code>: A mapping of public module names → local file paths. This is how you decide which parts of your code are available to be consumed remotely.</p>
</li>
</ul>
<pre><code class="lang-typescript">exposes: {
  <span class="hljs-string">"./Button"</span>: <span class="hljs-string">"./src/components/Button"</span>,
  <span class="hljs-string">"./Header"</span>: <span class="hljs-string">"./src/components/Header"</span>
}
</code></pre>
<p>The <strong>key</strong> (<code>"./Button"</code>) is the <strong>public module name</strong> – the name that other apps (the host) will use when importing the module from your remote.</p>
<h4 id="heading-how-it-works">How It Works:</h4>
<ul>
<li><p><strong>Key</strong> (<code>"./Button"</code>) is the exposed identifier other apps can request.</p>
</li>
<li><p><strong>Value</strong> (<code>"./src/components/Button"</code>) is the actual file path inside your project.</p>
</li>
</ul>
<p>For example, if your remote’s <code>name</code> is <code>"remote_app"</code>, the host can import like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"remote_app/Button"</span>;
</code></pre>
<p>Under the hood, <code>"remote_app"</code> matches the <strong>remote’s</strong> <code>name</code> in its <code>federation()</code> config and <code>Button</code> matches the <code>"./Button"</code> key in <code>exposes</code>.</p>
<ul>
<li><p><code>shared</code>: dependencies that should be <strong>shared</strong> between the host and remote. It avoids shipping duplicate copies of large libraries (like React), ensuring the host and remote use the same instance – you can read more <a target="_blank" href="https://module-federation.io/configure/shared">here</a> about the possible configurations.</p>
</li>
<li><p><code>remotes</code>: A mapping of remote app names → the URL to their <code>remoteEntry.js</code> file. This tells the host where to fetch the exposed modules at runtime.</p>
</li>
</ul>
<pre><code class="lang-typescript">remotes: { remote_app: <span class="hljs-string">"http://localhost:5001/assets/remoteEntry.js"</span> }
</code></pre>
<p>The key <code>remote_app</code> must match the <code>name</code> in the remote’s config.</p>
<p>The value is the full URL to the remote’s entry file (served in dev or deployed in prod). Remember we set <code>strictPort:true</code> earlier – this is why. We need to ensure we’re pointing at the correct domain &amp; port.</p>
<h2 id="heading-key-constraints-on-exported-modules">Key Constraints On Exported Modules</h2>
<h3 id="heading-naming-constraints-and-rules">Naming constraints and rules</h3>
<p><strong>The key in</strong> <code>exposes</code> (<code>"./Button"</code>):</p>
<ul>
<li><p>Must start with <code>./</code> (per Module Federation spec).</p>
</li>
<li><p>Must be unique within the remote.</p>
</li>
<li><p>Is case-sensitive.</p>
</li>
<li><p>This is the <strong>public module path</strong> the host will request.</p>
</li>
<li><p>Doesn’t have to match the filename, but matching is a good convention for ease of reading</p>
</li>
</ul>
<p><strong>The file you point to (</strong><code>"./src/components/Button"</code>):</p>
<ul>
<li><p>Can export default, named exports, or both.</p>
</li>
<li><p>The host can import default or named exports, same as any ES module:</p>
<pre><code class="lang-typescript">  <span class="hljs-comment">// Default export</span>
  <span class="hljs-keyword">import</span> MyButton <span class="hljs-keyword">from</span> <span class="hljs-string">'remote_app/Button'</span>;

  <span class="hljs-comment">// Named export</span>
  <span class="hljs-keyword">import</span> { SpecialButton } <span class="hljs-keyword">from</span> <span class="hljs-string">'remote_app/Button'</span>;
</code></pre>
</li>
</ul>
<p><strong>The import name:</strong></p>
<ul>
<li><p>Completely up to the host developer when importing a <strong>default</strong> export.</p>
</li>
<li><p>Must match exactly for <strong>named</strong> exports.</p>
</li>
</ul>
<h2 id="heading-how-to-create-the-remote-components">How to Create the Remote Components</h2>
<p>Ok, so you’ve created your project structure, and you’ve setup your <code>vite.config.ts</code> file to allow for exposing and consuming your shared assets. Next you will create the remote components.</p>
<h3 id="heading-button-component">Button Component</h3>
<p>Let’s say you want to create a button component which will be shared across all your host applications, as you want to keep consistency. You can do this as below:</p>
<p>Navigate to the <code>remote-app</code> folder and create a new file called <code>Button.tsx</code> in <code>src/components</code> this will ensure it matches the configured federation plugin.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// remote - ./src/components/Button.tsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">interface</span> ButtonProps {
  text: <span class="hljs-built_in">string</span>;
  onClick?: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
}

<span class="hljs-keyword">const</span> Button: React.FC&lt;ButtonProps&gt; = <span class="hljs-function">(<span class="hljs-params">{ text, onClick }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;button
      onClick={onClick}
      className=<span class="hljs-string">"px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600 hover:cursor-pointer"</span>
    &gt;
      {text}
    &lt;/button&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Button;
</code></pre>
<p>You now have a re-usable <code>Button</code> component which has some base styling but allows for configuration of what the button does by passing in a <code>onClick()</code> argument.</p>
<h3 id="heading-header-component">Header Component</h3>
<p>Sticking with the theme of consistency, you want to create a <code>&lt;header/&gt;</code> component which you can use on all your organisation’s websites, ensuring a themed appearance on all applications.</p>
<p>Like before, create a <code>Header.tsx</code> file within <code>src/components/</code>, and paste in the following code:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// remote - .src/components/Header.tsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> Header: React.FC = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;header className=<span class="hljs-string">"bg-gray-800 text-white p-4"</span>&gt;
      &lt;h1 className=<span class="hljs-string">"text-2xl"</span>&gt;Remote App Header&lt;/h1&gt;
      &lt;p className=<span class="hljs-string">"text-white"</span>&gt;Hi, Grant&lt;/p&gt;
    &lt;/header&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Header;
</code></pre>
<p>I’ve kept it simple, as this tutorial is for proof of concept purposes rather than aesthetic / real-world components.</p>
<h2 id="heading-how-to-consume-the-remote-components-within-the-host">How to Consume the Remote Components Within the Host</h2>
<p>You have your remote components created, so next you need to get them into your host application and begin using them. This is quite simple now that you’ve already setup your <code>vite.config.ts</code>.</p>
<p>You <strong>could</strong> import the components into your <code>App.tsx</code>, but this is not best practice as it can bloat your App.tsx (entry point component). I’ve opted to create a <code>RemoteWrapperComponent</code> which pulls in the remote components and handles the business logic.</p>
<p><code>RemoteComponentWrapper</code><strong>:</strong></p>
<p>Create a file called <code>RemoteComponentWrapper.tsx</code> in <code>src/components</code>, pasting the following code:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// host - ./src/components/RemoteComponentWrapper.tsx</span>
<span class="hljs-keyword">import</span> React, { Suspense } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> RemoteHeader = React.lazy(<span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">"remote_app/Header"</span>));
<span class="hljs-keyword">const</span> RemoteButton = React.lazy(<span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">"remote_app/Button"</span>));

<span class="hljs-keyword">const</span> LoadingSpinner = <span class="hljs-function">() =&gt;</span> (
  &lt;div className=<span class="hljs-string">"flex justify-center p-4"</span>&gt;
    &lt;div className=<span class="hljs-string">"animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"</span>&gt;&lt;/div&gt;
  &lt;/div&gt;
);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> RemoteComponentWrapper = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"p-4"</span>&gt;
      &lt;Suspense fallback={&lt;LoadingSpinner /&gt;}&gt;
        &lt;RemoteHeader /&gt;
      &lt;/Suspense&gt;

      &lt;div className=<span class="hljs-string">"mt-4"</span>&gt;
        &lt;Suspense fallback={&lt;LoadingSpinner /&gt;}&gt;
          &lt;RemoteButton
            text=<span class="hljs-string">"Remote Button"</span>
            onClick={<span class="hljs-function">() =&gt;</span>
              alert(
                <span class="hljs-string">"Well done you've imported the MF remote component successfully"</span>
              )
            }
          /&gt;
        &lt;/Suspense&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};
</code></pre>
<p>This component acts as a wrapper, handling some very simple business logic such as loading your remote components, handling a loading spinner whilst waiting for the remote, and passing your <code>onClick</code> event to the remote button.</p>
<h3 id="heading-why-use-reactlazy">Why Use <code>React.lazy()</code>?</h3>
<p>The import with <code>React.lazy</code> isn’t required for Module Federation components – it’s more of a best practice for React apps when the remote module is:</p>
<ul>
<li><p>Loaded asynchronously at runtime, which Module Federation remotes almost always are</p>
</li>
<li><p>You want React to handle the loading state and code-splitting gracefully – shown using the <code>Suspense</code> component</p>
</li>
</ul>
<p><code>React.lazy</code> + <code>&lt;Suspense&gt;</code> gives React a built-in way to pause rendering until the component is ready. Without it, you’d need manual loading state handling.</p>
<p>It also keeps your components looking “normal.” With React.Lazy, <code>&lt;RemoteHeader/&gt;</code> is just another component in your JSX.</p>
<p>Without it, you’d need something like:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> [Header, setHeader] = useState(<span class="hljs-literal">null</span>);

useEffect(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">import</span>(<span class="hljs-string">"remote_app/Header"</span>).then(<span class="hljs-function"><span class="hljs-params">m</span> =&gt;</span> setHeader(<span class="hljs-function">() =&gt;</span> m.default));
}, []);

<span class="hljs-keyword">return</span> Header ? &lt;Header /&gt; : &lt;LoadingSpinner /&gt;;
</code></pre>
<p>…which is messier and repeats for every remote component.</p>
<h2 id="heading-how-to-handle-typescript-errors">How to Handle TypeScript Errors</h2>
<p>Inside your <code>RemoteWrapperComponent</code> you’re going to see the following error on your <code>Button</code> and <code>Header</code> imports.</p>
<blockquote>
<p>Cannot find module 'remote_app/Button' or its corresponding type declarations.ts (2307)</p>
</blockquote>
<p>You get this error because the remote modules are not defined with types, so both your remote and your host doesn’t know what this imported component is, nor does it know its structure (a key part to TypeScript development).</p>
<p>To fix this you will need to provide your host app with custom types.</p>
<h3 id="heading-add-a-type-declaration-file">Add a Type Declaration File</h3>
<p>A type declaration file (if you’re unaware of them) has a <code>.d.ts</code> suffix.</p>
<p>Within your host app, create a file in <code>src/types</code> called <code>remote-app.d.ts</code>. Naming the file in this way lets us know the declarations within are related to the <em>remote-app</em>. This is useful especially when consuming multiple remotes.</p>
<p>Copy and paste the following declarations into your <code>remote-app.d.ts</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// host - ./src/types/remote.d.ts</span>
<span class="hljs-keyword">declare</span> <span class="hljs-keyword">module</span> "remote_app/Button" {
  <span class="hljs-keyword">const</span> Button: React.FC&lt;{
    text: <span class="hljs-built_in">string</span>;
    onClick?: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
  }&gt;;
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Button;
}

<span class="hljs-keyword">declare</span> <span class="hljs-keyword">module</span> "remote_app/Header" {
  <span class="hljs-keyword">const</span> Header: React.FC;
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Header;
}
</code></pre>
<p>Now if you return to your <code>RemoteWrapperComponent</code> your errors should be gone. If they aren’t, restart your IDE (in VS Code you can open your command palette and select <code>Restart Typescript Server</code>).</p>
<h2 id="heading-how-to-add-remotewrappercomponent-to-apptsx">How to Add <code>RemoteWrapperComponent</code> to App.tsx</h2>
<p>Import the <code>RemoteWrapperComponent</code> into App.tsx.</p>
<p>I’ve removed all the boilerplate code and replaced with some basic styling to allow us to easily see what is the host, and what are the remote components.</p>
<p>Copy and paste the following code into your host <code>App.tsx</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// host - ./src/App.tsx</span>
<span class="hljs-keyword">import</span> viteLogo <span class="hljs-keyword">from</span> <span class="hljs-string">"/vite.svg"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./index.css"</span>;
<span class="hljs-keyword">import</span> { RemoteComponentWrapper } <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/RemoteComponentWrapper"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;div className=<span class="hljs-string">"px-6 border-2"</span>&gt;
        &lt;div className=<span class="hljs-string">"flex justify-center items-center"</span>&gt;
          &lt;img src={viteLogo} alt=<span class="hljs-string">"Example"</span> /&gt;
        &lt;/div&gt;
        &lt;h1 className=<span class="hljs-string">"text-2xl"</span>&gt;Host Application&lt;/h1&gt;
        &lt;p&gt;
          {<span class="hljs-string">" "</span>}
          Welcome to the Host application, below are the components pulled <span class="hljs-keyword">from</span>
          the remote application
        &lt;/p&gt;
        &lt;RemoteComponentWrapper /&gt;
      &lt;/div&gt;
    &lt;/&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<h2 id="heading-how-to-serve-the-remote-app-and-run-your-host">How to Serve the Remote App and Run Your Host</h2>
<p>Due to how Vite works, you need to build the application before you preview / serve the application.</p>
<p>Make sure that your <code>package.json</code> file’s scripts block looks like this:</p>
<pre><code class="lang-json"># remote-app
<span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"vite"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"tsc -b &amp;&amp; vite build"</span>,
    <span class="hljs-attr">"lint"</span>: <span class="hljs-string">"eslint ."</span>,
    <span class="hljs-attr">"preview"</span>: <span class="hljs-string">"vite preview --port 5001 --strictPort"</span>,
    <span class="hljs-attr">"serve"</span>: <span class="hljs-string">"npm run build &amp;&amp; npm run preview"</span>
  },

# host-app
<span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"vite"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"tsc -b &amp;&amp; vite build"</span>,
    <span class="hljs-attr">"lint"</span>: <span class="hljs-string">"eslint ."</span>,
    <span class="hljs-attr">"preview"</span>: <span class="hljs-string">"vite preview --port 5000 --strictPort"</span>,
    <span class="hljs-attr">"serve"</span>: <span class="hljs-string">"npm run build &amp;&amp; npm run preview"</span>
  },
</code></pre>
<p>In your terminal run:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> ./remote-app

<span class="hljs-comment"># run a build, and run / load the app on port 5000</span>
npm run serve
</code></pre>
<p>You should see something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755207471886/c64b29ef-3f95-44e5-903b-896f7a3a36fc.png" alt="screenshot showing output from terminal, port 5001 running remote app" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Now you can run the host application by doing the same:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># change to the host-app in another terminal </span>
<span class="hljs-built_in">cd</span> ../host-app

<span class="hljs-comment"># build and run the application</span>
npm run serve
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755207573717/77ab133f-ac2e-4f51-8152-3de7200c067c.png" alt="screenshot showing the host-app running on port 5000" class="image--center mx-auto" width="868" height="302" loading="lazy"></p>
<p>If you open the <code>localhost:5000</code> you should now see your host application with the remote components:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756078239732/775ab6ec-4566-44cd-ac85-66c37a9001ca.png" alt="image: shows the finished host app consuming the remote components" class="image--center mx-auto" width="2524" height="646" loading="lazy"></p>
<p>Next, if you click the button, you can see that it shows the message you configured from within the <code>RemoteWrapperComponent</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756078270491/65a72ab8-1cfa-4298-be02-68d0528fd50f.png" alt="image: shows alert modal with provided message after clicking remote button" class="image--center mx-auto" width="1948" height="728" loading="lazy"></p>
<h2 id="heading-the-true-power-of-micro-frontends">The True Power of Micro Frontends</h2>
<p>The true power of micro frontends lies in their ability to update the remote components, without the need to rebuild the host. To fully demonstrate this, keep the host running, and update the <code>Button</code> component on your <code>remote-app</code>.</p>
<p>Let’s update the remote components. Use the below code to update both the <code>Button</code> and <code>Header</code> components:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// remote - ./src/components/Header.tsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> Header: React.FC = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;header className=<span class="hljs-string">"bg-gray-800 text-white p-4"</span>&gt;
      &lt;h1 className=<span class="hljs-string">"text-2xl"</span>&gt;Updated Remote App Header&lt;/h1&gt;
      &lt;p className=<span class="hljs-string">"text-white"</span>&gt;Hi, Grant&lt;/p&gt;
    &lt;/header&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Header;

<span class="hljs-comment">// remote - ./src/components/Button.tsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">interface</span> ButtonProps {
  text: <span class="hljs-built_in">string</span>;
  onClick?: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
}

<span class="hljs-keyword">const</span> Button: React.FC&lt;ButtonProps&gt; = <span class="hljs-function">(<span class="hljs-params">{ text, onClick }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;button
      onClick={onClick}
      className=<span class="hljs-string">"px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 hover:cursor-pointer"</span>
    &gt;
      {text}
    &lt;/button&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Button;
</code></pre>
<p>Once you’ve updated the remote components, run the following command in your <code>remote-app</code> folder:</p>
<pre><code class="lang-bash">npm run serve
</code></pre>
<p>Next, refresh your host app in the browser and you will see the updated app:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756078980661/02ba0655-a18c-460c-939b-6fa95b4149b7.png" alt="image: shows host application with updated remote compnents" class="image--center mx-auto" width="2514" height="664" loading="lazy"></p>
<p>The update to the remote components is visible immediately, without restarting or rebuilding the host app. This highlights a key benefit of micro frontends: shared components are fetched from their own server via the <code>remote.js</code> file, enabling independent updates.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>You've successfully built and deployed a micro frontend architecture – congrats! This basic implementation demonstrates the true power of Module Federation and the ability to update shared components without needing to rebuild and redeploy the entire host application.</p>
<p>This independence can dramatically accelerate development cycles and empower teams to work more autonomously.</p>
<p>I hope you’ve learned something from this article, and as always for more tutorials and discussions, connect with me on <a target="_blank" href="https://x.com/grantdotdev">twitter/x</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What Are JSON Web Tokens (JWT)? ]]>
                </title>
                <description>
                    <![CDATA[ When you’re working with any website, application, or API, you'll inevitably need to log in and authenticate your user base. One of the more commonly used methods of passing around authentication credentials from one system to another is using a JSON... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-are-json-web-tokens-jwt/</link>
                <guid isPermaLink="false">686c430b1a4ac707c0692874</guid>
                
                    <category>
                        <![CDATA[ JWT ]]>
                    </category>
                
                    <category>
                        <![CDATA[ authentication ]]>
                    </category>
                
                    <category>
                        <![CDATA[ authorization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Mon, 07 Jul 2025 21:58:35 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1751819356361/352ef68a-fa20-4a69-b666-393f7a17fa40.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you’re working with any website, application, or API, you'll inevitably need to log in and authenticate your user base. One of the more commonly used methods of passing around authentication credentials from one system to another is using a JSON Web Token (JWT).</p>
<p>In this article, you'll learn about:</p>
<ul>
<li><p>What a JSON Web Token (JWT) is</p>
</li>
<li><p>How JWTs are structured and created</p>
</li>
<li><p>Different JWT signing techniques and algorithms (Symmetric vs. Asymmetric)</p>
</li>
<li><p>How JWTs are used in real-world authentication flows</p>
</li>
<li><p>Important security best practices for using JWTs</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-a-jwt">What is a JWT?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-json-web-tokens-are-made-of-three-elements">JSON Web Tokens Are Made Of Three Elements</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-asymmetric-signing-rs256-explained">Asymmetric Signing (RS256) Explained</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-analogy-the-lock-and-key-for-asymmetric-signatures">Analogy: The Lock and Key for Asymmetric Signatures</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-symmetric-signing-hs256-hmac-with-sha-256">Symmetric Signing: HS256 (HMAC with SHA-256)</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-how-hs256-verification-works">How HS256 Verification Works</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-jwts-in-action-a-typical-authentication-flow">JWTs in Action: A Typical Authentication Flow</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-jwt-security-best-practices-amp-considerations">JWT Security Best Practices &amp; Considerations</a></p>
</li>
</ul>
<h2 id="heading-what-is-a-jwt">What Is a JWT?</h2>
<blockquote>
<p>JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.<br>— Introduction to JWT by jwt.io</p>
</blockquote>
<p>While accurate, this definition can be a bit dense at first glance. Imagine you want to send someone a sealed, tamper-proof message. That's essentially what a JSON Web Token (JWT) is. It's a secure message, a special kind of message designed to be sent between two parties which can be assured it came from an expected sender.</p>
<p>Each JWT is digitally signed using either a <strong>secret code</strong> (for symmetric algorithms like HMAC) or a <strong>private key</strong> (for asymmetric algorithms like RSA or ECDSA).</p>
<p>This secret code or private key is known only to the system that issues the JWT (often called an <em>authentication provider</em>, like Auth0, AWS Cognito, or Firebase Auth, which handles user logins and identity).</p>
<p>This signature proves two things:</p>
<ol>
<li><p><strong>Authenticity:</strong> It proves the message really came from who it claims to be from.</p>
</li>
<li><p><strong>Integrity:</strong> It proves that the message hasn't been changed or tampered with since it was signed. If even one character is altered, the signature won't match, and you'll know something is wrong, meaning the contents of the JWT can't be trusted.</p>
</li>
</ol>
<h3 id="heading-json-web-tokens-are-made-of-three-elements">JSON Web Tokens Are Made of Three Elements</h3>
<p>JWTs are made up of 3 key parts:</p>
<ol>
<li><p>Header</p>
</li>
<li><p>Payload</p>
</li>
<li><p>Signature</p>
</li>
</ol>
<h4 id="heading-header">Header</h4>
<p>The header contains metadata information about the token. Think of it like a label on a package – it tells you what’s inside and how it was prepared.</p>
<p>Typically, the header contains:</p>
<p><code>alg</code>: This specifies the <strong>algorithm</strong> used to sign the JWT. Common algorithms are <code>HS256</code> (HMAC with SHA-256) or <code>RS256</code> (RSA with SHA-256).</p>
<p><code>typ</code>: This specifies the <strong>type</strong> of token, which is almost always <code>JWT</code> for standard JSON Web Tokens.</p>
<p><strong>Example (decoded):</strong></p>
<pre><code class="lang-json">{ 
  <span class="hljs-attr">"alg"</span>: <span class="hljs-string">"RS256"</span>,
  <span class="hljs-attr">"typ"</span>: <span class="hljs-string">"JWT"</span> 
}
</code></pre>
<h4 id="heading-payload">Payload</h4>
<p>This is the second part of the JWT, and it's where the real data or "claims" are stored. Claims are statements about an entity (usually a user) and any other additional data. Using the previous analogy of a package, think of it as the “contents” of the package.</p>
<p>There are three types of claims:</p>
<p><strong>1. Registered Claims:</strong> These are predefined claims that are recommended for common use cases. They are not mandatory but are very useful for interoperability. These include:</p>
<ul>
<li><p><code>iss</code> – issuer, who issued the token (for example, your application’s domain)</p>
</li>
<li><p><code>sub</code> – subject, the subject of the token (for example, a User’s ID)</p>
</li>
<li><p><code>aud</code> – audience, the audience of the token (that is, who the token is intended for – for example, a specific API)</p>
</li>
<li><p><code>exp</code> – <strong>expiration</strong>, the expiry date as a timestamp</p>
</li>
<li><p><code>iat</code> – issued at, when the token was issued as a timestamp</p>
</li>
<li><p><code>nbf</code> – not before, when the token becomes valid (that is, the token cannot be used or deemed valid before this timestamp)</p>
</li>
<li><p><code>jti</code> – JWT ID, a unique identifier for the token, useful for preventing replay attacks or blacklisting</p>
</li>
</ul>
<p><strong>2. Public Claims:</strong> These can be defined by anyone using JWTs. To avoid naming conflicts, it's a good practice to register them or define them using a unique identifier like a URI.</p>
<p><strong>3. Private Claims:</strong> These are custom claims created to share specific information between parties who agree on using them. They are entirely up to you and your application's needs.</p>
<p><strong>Example payload:</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"sub"</span>: <span class="hljs-string">"1234567890"</span>, <span class="hljs-comment">//  subject</span>
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"John Doe"</span>, <span class="hljs-comment">// private claim</span>
  <span class="hljs-attr">"admin"</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// private claim / role</span>
  <span class="hljs-attr">"iat"</span>: <span class="hljs-number">1678886400</span>, <span class="hljs-comment">// Issued at a specific timestamp</span>
  <span class="hljs-attr">"exp"</span>: <span class="hljs-number">1678890000</span>  <span class="hljs-comment">// Expires at a specific timestamp</span>
}
</code></pre>
<p>Like the header, this JSON object is also <strong>Base64Url encoded</strong> (a URL-safe variant of Base64 encoding) to form the second part of the JWT string.</p>
<p><strong>Important Note:</strong> <em>The payload is</em> <strong><em>encoded*</em></strong>, not encrypted.* This means that anyone can easily decode the JWT and read its contents. Never put sensitive information (like passwords) directly into the payload unless the entire JWT itself is encrypted (which is a separate process called JWE - JSON Web Encryption). The security of a standard JWT comes entirely from the signature, which prevents tampering.</p>
<h4 id="heading-signature">Signature</h4>
<p>The signature, as we've already discussed, is the most important part of the JWT. Without it, there's no protection applied to the JWT, meaning no way to validate the origin of the token or its integrity.</p>
<p>The signature is created by taking the <strong>encoded header</strong>, the <strong>encoded payload</strong>, and a <strong>secret key</strong> (or a private key if using asymmetric algorithms like RSA). These are then run through the cryptographic algorithm specified in the header (<code>alg</code> field). For HS256, a shared secret key is used. For RS256, a private key is used to sign, and a corresponding public key is used to verify. We’ll get on to verification soon.</p>
<p>Think of it like a tamper-proof seal on your package, or even better, a wax seal on a letter. If you receive your letter and the wax seal has been broken, you'd naturally believe the contents of the letter may not be original and therefore can't be trusted.</p>
<p>In pseudo-code it would look like this:</p>
<p><code>Signature = Algorithm( Base64Url(Header) + "." + Base64Url(Payload), SecretKey )</code></p>
<p>The result of this signing process is the signature, which is also Base64Url encoded to form the third part of the JWT string.</p>
<p>At the end of the whole process your JWT would look like this:</p>
<p><code>base64EncodedHeader.base64EncodedPayload.base64EncodedSignature</code></p>
<h3 id="heading-asymmetric-signing-rs256-explained">Asymmetric Signing (RS256) Explained</h3>
<p>When a JWT uses an algorithm like RS256 (RSA Signature with SHA-256), it employs an <strong>asymmetric cryptographic</strong> process involving a <strong>public</strong> and <strong>private</strong> key pair. This is where the core magic of proving authenticity and integrity happens without needing to share a secret.</p>
<h4 id="heading-the-signing-process-by-the-issuer">The Signing Process (by the Issuer)</h4>
<p>The <strong>sender</strong> (the server that issues the JWT, like Auth0) possesses the <strong>private key</strong>. This key is kept absolutely secret and secure. Here are the steps:</p>
<ol>
<li><p><strong>Prepare the data:</strong> The server takes the header (which includes the algorithm) and the payload. It Base64Url-encodes them, and then concatenates them with a dot: <code>Base64Url(Header) + "." + Base64Url(Payload)</code>.</p>
<p> For example, with this header:</p>
<pre><code class="lang-json"> {
   <span class="hljs-attr">"typ"</span>: <span class="hljs-string">"JWT"</span>,
   <span class="hljs-attr">"alg"</span>: <span class="hljs-string">"RS256"</span>
 }
</code></pre>
<p> And this payload:</p>
<pre><code class="lang-json"> {
   <span class="hljs-attr">"sub"</span>: <span class="hljs-string">"1234567890"</span>,
   <span class="hljs-attr">"name"</span>: <span class="hljs-string">"John Doe"</span>,
   <span class="hljs-attr">"admin"</span>: <span class="hljs-literal">true</span>,
   <span class="hljs-attr">"iat"</span>: <span class="hljs-number">1751494086</span>,
   <span class="hljs-attr">"exp"</span>: <span class="hljs-number">1751497686</span>
 }
</code></pre>
<p> This would create a Base64Url-encoded <code>header.payload</code> string like the animated example below:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751810671075/128fa652-fe2a-4413-a238-71531bfe67ae.gif" alt="Animated gif of the process converting header and payload to base64 encoded string" class="image--center mx-auto" width="800" height="587" loading="lazy"></p>
</li>
<li><p><strong>Calculate the hash:</strong> It then calculates a <strong>hash</strong> (using SHA-256 in this case) of this combined header and payload string.</p>
</li>
<li><p><strong>Sign the hash:</strong> Finally, it signs this hash using its private key. This cryptographically transformed hash is the signature part of the JWT.</p>
</li>
</ol>
<p>The JWT is then formed by concatenating the Base64Url-encoded header, the Base64Url-encoded payload, and the Base64Url-encoded signature, separated by dots: <code>header.payload.signature</code>.</p>
<p>The top segment below shows the full JWT token (header.payload.signature):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751830473890/31da21a0-50db-4a48-b4c3-378c2ac1616a.png" alt="Image displaying the fully formed JWT token along with original header and payload" class="image--center mx-auto" width="2630" height="1100" loading="lazy"></p>
<h3 id="heading-the-verification-process">The Verification Process</h3>
<p>This is where the magic happens, and it's often a point of confusion. The public key doesn't "decrypt" the original data like a symmetric key does. Instead, it performs a unique <strong>verification</strong> process.</p>
<p>The receiver (the client or another server that needs to verify the JWT) possesses the <strong>public key</strong>. This key does <strong>not</strong> need to be kept secret – it can be freely distributed.</p>
<p>Here's a step-by-step explanation:</p>
<ol>
<li><p><strong>Separate the parts:</strong> The first thing the receiver does is split the incoming JWT string into its three Base64Url-encoded components: the Header, the Payload, and the Signature.</p>
</li>
<li><p><strong>Obtain the public key:</strong> The verifier needs the <strong>public key</strong> that corresponds to the private key used by the issuer. Public keys are often available via a <strong>JWKS (JSON Web Key Set) endpoint</strong> (for example, <code>your-domain.com/.well-known/jwks.json</code>).</p>
</li>
<li><p><strong>Re-create the data to be hashed:</strong> The receiver takes the received, Base64Url-encoded Header and the received, Base64Url-encoded Payload. It then combines them exactly as the issuer did: <code>EncodedHeader.EncodedPayload</code>.</p>
</li>
<li><p><strong>Compute a local hash (Hash A):</strong> This combined string is then put through the same hashing algorithm (for example, SHA-256) that was specified in the JWT's header. This produces a new, locally computed hash (let's call this <strong>"Hash A"</strong>). This local hash represents what the content <em>should</em> look like if it hasn't been tampered with.</p>
</li>
<li><p><strong>"Unsign" the received signature with the public key to get the original signed hash (Hash B):</strong> This is the core cryptographic step. The verifier uses the public key (obtained in step 2) to perform a mathematical operation on the received signature. This operation does <em>not</em> create a new signature for comparison. Instead, it effectively "unsigns" or "decrypts" the signature to reveal the <strong>original hash ("Hash B")</strong> that was produced by the issuer's private key.</p>
<ul>
<li><strong>Crucial Point:</strong> This process is for <strong>verifying authenticity</strong>, not decrypting confidential data. The public key confirms that the signature was indeed created by the corresponding private key, and as part of that confirmation, it returns the original hash that <em>was signed</em>.</li>
</ul>
</li>
<li><p><strong>Compare the hashes:</strong> The verifier now has two hashes:</p>
<ul>
<li><p><strong>Hash A:</strong> The hash it <strong>computed locally</strong> from the received header and payload (from step 4).</p>
</li>
<li><p><strong>Hash B:</strong> The original hash that was extracted from the received Signature using the public key (from step 5)</p>
</li>
</ul>
</li>
<li><p><strong>If Hash A matches Hash B:</strong> It proves two critical things:</p>
<ol>
<li><p><strong>Authenticity:</strong> The token was indeed signed by the legitimate holder of the corresponding private key (for example, Auth0).</p>
</li>
<li><p><strong>Integrity:</strong> The content of the header and payload has <strong>not been tampered with</strong> since it was originally signed. If even a single character in the header or payload were changed, Hash A would be different, and it would not match Hash B. In this case, the JWT is considered valid and its contents can be trusted.</p>
</li>
</ol>
</li>
<li><p><strong>If the hashes do NOT match:</strong> The token is considered invalid and <strong>must be rejected</strong>. This indicates either that the JWT was signed by an unauthorised party (a forged token) or that its header or payload has been altered after it was signed.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751813812291/de32c7b0-1f3c-4f26-995e-b5c618b104b3.png" alt=" Flow diagram of asymmetric verification process" class="image--center mx-auto" width="1649" height="2947" loading="lazy"></p>
<h3 id="heading-analogy-the-lock-and-key-for-asymmetric-signatures">Analogy: The Lock and Key for Asymmetric Signatures</h3>
<p><strong>Private Key:</strong> A special, unique key that can <strong>lock</strong> a box (create a signature). Only the owner has this key.</p>
<p><strong>Public Key:</strong> A widely distributed key that can <strong>test</strong> if a box was locked by the corresponding private key. It can't lock a new box, but it can confirm if an existing lock is authentic.</p>
<p>You don't re-lock the box with the public key. You use the public key to check if the existing lock (the signature) is genuine and corresponds to the contents of the box.</p>
<h2 id="heading-symmetric-signing-hs256-hmac-with-sha-256">Symmetric Signing: HS256 (HMAC With SHA-256)</h2>
<p>While RS256 uses a pair of keys (private for signing, public for verifying), many JWTs you'll encounter are signed symmetrically, most commonly with the HS256 algorithm. HS256 stands for <strong>HMAC (Hash-based Message Authentication Code) with SHA-256</strong>.</p>
<p>The fundamental difference here is the use of a single, shared secret key for <em>both</em> signing and verification.</p>
<h3 id="heading-how-hs256-signing-works">How HS256 Signing Works</h3>
<ol>
<li><p><strong>Shared secret key:</strong> The issuer (for example, your authentication provider) possesses a single, confidential secret key. This key is known <em>only</em> to the issuer and any parties (like your API) that need to verify the token.</p>
</li>
<li><p><strong>Combine header and payload:</strong> Just like with asymmetric signing, the issuer takes the Base64Url-encoded Header (which specifies <code>"alg": "HS256"</code>) and the Base64Url-encoded Payload, and <strong>joins</strong> them with a dot.</p>
</li>
<li><p><strong>Apply HMAC-SHA256:</strong> This combined string is then fed into the HMAC-SHA256 algorithm along with the secret key. The HMAC algorithm uses the secret key to create a unique hash (the signature) of the data. In pseudo-code, it looks like this:</p>
<p> <code>Signature = HMAC-SHA256( Base64Url(Header) + "." + Base64Url(Payload), SecretKey )</code></p>
</li>
<li><p><strong>Form the JWT:</strong> The resulting signature (which is also Base64Url-encoded) is appended to the header and payload with a dot, forming the complete JWT: <code>base64EncodedHeader.base64EncodedPayload.base64EncodedSignature</code>.</p>
</li>
</ol>
<h3 id="heading-how-hs256-verification-works">How HS256 Verification Works</h3>
<p>When a receiver gets an HS256-signed JWT, it goes through a verification process.</p>
<p>First, it separates the parts. The JWT is split into its three Base64Url-encoded components: Header, Payload, and Signature, as we did with asymmetric JWTs.</p>
<p>Then, it obtains the shared secret key. The receiver must also possess the <strong>exact same secret key</strong> that the issuer used to sign the token. This key is <em>not</em> publicly distributed like a public key – it must be securely provisioned to any entity that needs to verify tokens.</p>
<p>Next, it re-calculates the signature. The receiver does this by taking the received Base64Url-encoded Header and Payload, combining them, and then re-applying the HMAC-SHA256 algorithm using the <em>same secret key</em>. This produces a new, locally computed signature.</p>
<p>Finally, the receiver compares the signature it just calculated locally with the signature it received as part of the JWT.</p>
<ul>
<li><p><strong>If the two signatures match:</strong> The token is considered valid. This confirms its authenticity (it came from someone who knows the secret) and integrity (it hasn't been tampered with).</p>
</li>
<li><p><strong>If the signatures do NOT match:</strong> The token is invalid and must be rejected. This indicates either tampering or that it was signed with a different, unknown secret key.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751814851136/a73b7af6-e92d-40f3-b1e3-c4bd2406ede9.png" alt="Flow diagram of symmetric verification process" class="image--center mx-auto" width="1715" height="2977" loading="lazy"></p>
<h3 id="heading-key-differences-and-considerations">Key Differences and Considerations:</h3>
<ul>
<li><p><strong>Key management:</strong> With HS256, the secret key must be securely shared and kept confidential by <em>all</em> parties involved in both signing and verifying. This can be more challenging to manage securely at scale compared to the public/private key model, where only the private key needs strict secrecy.</p>
</li>
<li><p><strong>Performance:</strong> HS256 is generally faster to compute than asymmetric algorithms like RS256, making it suitable for high-volume scenarios where the secret key can be securely distributed.</p>
</li>
</ul>
<h2 id="heading-jwts-in-action-a-typical-authentication-flow">JWTs in Action: A Typical Authentication Flow</h2>
<p>Now that you understand how JWTs are structured and signed, let's look at how they're typically used in a real-world web application. This authentication flow is a common pattern you'd encounter.</p>
<h3 id="heading-step-1-user-logs-in"><strong>Step 1: User Logs In:</strong></h3>
<p>A user opens a client application (for example, a web browser, mobile app) and enters their login credentials (username and password).</p>
<p>The client sends these credentials securely (always over HTTPS!) to an <strong>authentication server</strong> (like Auth0, AWS Cognito, or your own backend's authentication endpoint).</p>
<h3 id="heading-step-2-authentication-server-issues-jwt"><strong>Step 2: Authentication Server Issues JWT:</strong></h3>
<p>Then the authentication server verifies the user's credentials. If valid, it generates a new JWT. This JWT contains claims (like the user's ID, roles, expiration time) in its payload and is digitally signed by the server's <strong>private key</strong> (for asymmetric algorithms like RS256) or <strong>secret key</strong> (for symmetric algorithms like HS256).</p>
<p>The server then sends this signed JWT back to the client.</p>
<h3 id="heading-step-3-client-stores-jwt"><strong>Step 3: Client Stores JWT:</strong></h3>
<p>The client receives the JWT and typically stores it in a secure location, such as browser memory storage, session storage, or an HTTP-only cookie. The method of storage depends on the client type and security considerations.</p>
<h3 id="heading-step-4-client-makes-api-calls"><strong>Step 4: Client Makes API Calls:</strong></h3>
<p>When the user wants to access a protected resource on a backend API (for example, their profile data, a private feed), the client includes the JWT in the request.</p>
<p>The standard way to do this is by sending the token in the <code>Authorization</code> header of the HTTP request, prefixed with the word <code>Bearer</code>:</p>
<p><code>Authorization: Bearer &lt;your_jwt_here&gt;</code></p>
<h3 id="heading-step-5-api-verifies-jwt-amp-authorises-request"><strong>Step 5: API Verifies JWT &amp; Authorises Request:</strong></h3>
<p>Now, the backend API receives the request and extracts the JWT from the <code>Authorization</code> header. The API then performs the JWT verification process depending on the algorithm:</p>
<ul>
<li><p>It checks the token's claims, especially the <code>exp</code> (expiration) claim, to ensure it's still valid.</p>
</li>
<li><p>If the token is valid, the API trusts the claims within the payload (for example, the user's ID) and proceeds to fulfill the request, potentially using the user's roles to determine if they have permission to access the requested resource.</p>
</li>
<li><p>If the token is invalid (bad signature, expired, and so on), the API rejects the request, typically with an HTTP 401 Unauthorised status.</p>
</li>
</ul>
<p>This flow is powerful because JWTs are <strong>stateless</strong>: once issued, the authentication server doesn't need to keep a record of active sessions. The API can verify the token independently, which simplifies scaling and reduces server load.</p>
<h2 id="heading-jwt-security-best-practices-and-considerations">JWT Security Best Practices and Considerations</h2>
<p>While JWTs offer powerful authentication capabilities, using them securely requires careful attention to best practices. Misconfigurations or oversight can lead to significant vulnerabilities.</p>
<h3 id="heading-always-use-httpstls"><strong>Always Use HTTPS/TLS:</strong></h3>
<p><strong>Crucial:</strong> JWTs are <strong>encoded, not encrypted, by default</strong>. This means anyone who intercepts the token during transmission can easily read its payload. Therefore, JWTs (and all authentication traffic) <strong>must always be transmitted over HTTPS (TLS)</strong> to encrypt the communication channel itself and prevent eavesdropping.</p>
<h3 id="heading-protect-your-signing-keys"><strong>Protect Your Signing Keys:</strong></h3>
<p>Whether it's a private key (for RS256) or a shared secret key (for HS256), these keys are paramount. If an attacker gains access to your signing key, they can forge valid JWTs, impersonate users, and compromise your system. Store these keys securely, preferably in dedicated key management services.</p>
<h3 id="heading-keep-access-tokens-short-lived-exp-claim"><strong>Keep Access Tokens Short-Lived (</strong><code>exp</code> claim):</h3>
<p>You should always set short expiration times (for example, 5-15 minutes) for your JWTs used as access tokens. This minimises the window of opportunity for an attacker if a token is compromised.</p>
<p>Since JWTs are stateless, they are hard to revoke immediately once issued. A short lifespan is your primary defense against compromised tokens.</p>
<h3 id="heading-implement-refresh-tokens-for-longer-sessions"><strong>Implement Refresh Tokens (for Longer Sessions):</strong></h3>
<p>To maintain user experience with short-lived access tokens, use <strong>refresh tokens</strong>. A refresh token is a separate, longer-lived token (usually stored more securely) that can be exchanged for a new, short-lived access token when the current one expires, without requiring the user to re-authenticate. Refresh tokens <em>can</em> be revoked by the server, offering better control.</p>
<h3 id="heading-never-put-sensitive-data-in-the-payload"><strong>Never Put Sensitive Data in the Payload:</strong></h3>
<p>Reiterating this crucial point: the JWT payload is Base64Url encoded, which is easily reversible. Do not put passwords, highly sensitive PII (Personally Identifiable Information), or confidential business data directly into the JWT payload. Only include non-sensitive or publicly available information, or data that's already encrypted by other means.</p>
<h3 id="heading-validate-all-claims-on-verification"><strong>Validate ALL Claims on Verification:</strong></h3>
<p>When verifying a JWT, don't just check the signature. Always validate all relevant claims, including:</p>
<ul>
<li><p><code>exp</code> (Expiration): Ensure the token hasn't expired.</p>
</li>
<li><p><code>iss</code> (Issuer): Verify the token came from the expected authentication server.</p>
</li>
<li><p><code>aud</code> (Audience): Ensure the token is intended for your specific API/application.</p>
</li>
<li><p><code>nbf</code> (Not Before): Check if the token is active yet.</p>
</li>
</ul>
<h3 id="heading-consider-token-revocation-for-critical-cases"><strong>Consider Token Revocation (for critical cases):</strong></h3>
<p>For situations requiring immediate revocation (for example, user password change, account deactivation), typical stateless JWTs are challenging. Strategies include:</p>
<ul>
<li><p>Short expiration times (as above).</p>
</li>
<li><p>A blacklist/revocation list: Store the <code>jti</code> (JWT ID) of revoked tokens in a database, checking this list on every request. This adds a stateful lookup but provides immediate revocation.</p>
</li>
</ul>
<h2 id="heading-thanks-for-reading">Thanks for reading!</h2>
<p>I hope you’ve found this tutorial useful, and as always if you want to ask any questions or hear about upcoming articles, you can always follow me on ‘X’, my handle is @grantdotdev and follow by clicking <a target="_blank" href="https://x.com/grantdotdev">here</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create Accessible and User-Friendly Forms in React ]]>
                </title>
                <description>
                    <![CDATA[ When designing web applications, you’ll often be asked the age old question “How accessible is your website” and “Does it offer the best user experience?”. These are both very valid questions, but they are often overlooked in favour of rich or fancy ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-accessible-and-user-friendly-forms-in-react/</link>
                <guid isPermaLink="false">6810f59e8deee87383c1bc46</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Accessibility ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Tue, 29 Apr 2025 15:51:58 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745789677789/c386af23-39d6-4421-9f26-f98d75a30d61.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When designing web applications, you’ll often be asked the age old question “How accessible is your website” and “Does it offer the best user experience?”. These are both very valid questions, but they are often overlooked in favour of rich or fancy looking features, reducing the site’s audience.</p>
<p>In this article, I’ll teach you about the React Hook Form library, HTML attributes, and development considerations to make sure your site’s available for all, focusing on:</p>
<ul>
<li><p>blind or visually impaired users, who may use a screen reader</p>
</li>
<li><p>better user feedback</p>
</li>
<li><p>visual queues for all</p>
</li>
<li><p>design considerations for all</p>
</li>
</ul>
<p>Whilst following along with this tutorial, you can either pull down the code from the GitHub repo (by visiting this <a target="_blank" href="https://github.com/grant-dot-dev/form_accessibility_ux">page</a>), or you can use the inline code snippets within the article.</p>
<h3 id="heading-pre-requisites-for-this-article"><strong>Pre-requisites for this article:</strong></h3>
<ul>
<li><p>Knowledge of React</p>
</li>
<li><p>Knowledge of writing TypeScript and HTML / JSX.</p>
</li>
<li><p>Familiarity with Tailwind CSS (not required in order to follow this tutorial)</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-the-initial-basic-form">The Initial Basic Form</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-error-handling-with-react-hook-form">Error Handling With React-Hook-Form</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-hooking-up-the-useform-methods-to-our-form">Hooking Up The useForm Methods To Our Form</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-showing-error-messages">Showing Error Messages</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-adding-aria-required">Adding aria-required</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-adding-fieldset-and-legend">Adding fieldset and legend</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-adding-labels-and-using-htmlfor">Adding Labels and Using htmlFor</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-do-not-rely-on-placeholders-only">Do Not Rely on Placeholders Only!</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-give-additional-information-with-aria-describedby">Give Additional Information With aria-describedBy</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-avoid-tooltips-for-critical-information">Avoid Tooltips for Critical Information</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tell-me-something-important">Tell Me Something Important</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-focus-states-and-colouring">Focus States and Colouring</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-make-buttons-descriptive">Make Buttons Descriptive</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-the-initial-basic-form">The Initial Basic Form</h2>
<p>So if we take a look at the form in its current state, you may think it looks fine. But it’s actually not very accessible, nor does it offer a great user experience.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { TvIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">"@heroicons/react/24/outline"</span>;

<span class="hljs-keyword">type</span> FormData = {
    fullName: <span class="hljs-built_in">string</span>;
    email: <span class="hljs-built_in">string</span>;
    password: <span class="hljs-built_in">string</span>;
    confirmPassword: <span class="hljs-built_in">string</span>;
    agreeToTerms: <span class="hljs-built_in">boolean</span>;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> RegistrationForm = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> onSubmit = <span class="hljs-function">() =&gt;</span> {
        alert(<span class="hljs-string">`Form submitted`</span>);
    };

    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">"flex justify-center items-center w-screen h-screen bg-gray-900"</span>&gt;
            &lt;div className=<span class="hljs-string">"w-full max-w-md p-8 bg-black bg-opacity-75 rounded-lg"</span>&gt;
                &lt;div className=<span class="hljs-string">"flex flex-row justify-center items-center gap-x-4"</span>&gt;
                    &lt;TvIcon className=<span class="hljs-string">"h-12 w-12 text-white"</span> /&gt;
                    &lt;h1 className=<span class="hljs-string">"text-7xl font-bold text-center text-red-600 mb-4"</span>&gt;Getflix&lt;/h1&gt;
                &lt;/div&gt;
                &lt;h2 className=<span class="hljs-string">"text-3xl font-bold text-white mb-6 text-center"</span>&gt;
                    Sign Up
                &lt;/h2&gt;

                &lt;form onSubmit={onSubmit} className=<span class="hljs-string">"space-y-6"</span>&gt;

                    {<span class="hljs-comment">/* Full Name */</span>}
                    &lt;div&gt;
                        &lt;input
                            <span class="hljs-keyword">type</span>=<span class="hljs-string">"text"</span>
                            placeholder=<span class="hljs-string">"Full Name"</span>
                            className=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 "</span>
                        /&gt;

                    &lt;/div&gt;

                    {<span class="hljs-comment">/* Email */</span>}
                    &lt;div&gt;
                        &lt;input
                            <span class="hljs-keyword">type</span>=<span class="hljs-string">"email"</span>
                            placeholder=<span class="hljs-string">"Email Address"</span>
                            className=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 "</span>
                        /&gt;

                    &lt;/div&gt;

                    {<span class="hljs-comment">/* Password */</span>}
                    &lt;div&gt;
                        &lt;input
                            <span class="hljs-keyword">type</span>=<span class="hljs-string">"password"</span>
                            placeholder=<span class="hljs-string">"Password"</span>
                            className=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400"</span>
                        /&gt;

                    &lt;/div&gt;

                    {<span class="hljs-comment">/* Confirm Password */</span>}
                    &lt;div&gt;
                        &lt;input
                            <span class="hljs-keyword">type</span>=<span class="hljs-string">"password"</span>
                            placeholder=<span class="hljs-string">"Confirm Password"</span>
                            className=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 "</span>
                        /&gt;

                    &lt;/div&gt;

                    {<span class="hljs-comment">/* Agree to Terms */</span>}
                    &lt;div className=<span class="hljs-string">"flex items-center text-gray-400 text-sm"</span>&gt;
                        &lt;input

                            <span class="hljs-keyword">type</span>=<span class="hljs-string">"checkbox"</span>
                            id=<span class="hljs-string">"agreeToTerms"</span>
                            className=<span class="hljs-string">"mr-2"</span>
                        /&gt;
                        &lt;label htmlFor=<span class="hljs-string">"agreeToTerms"</span> className=<span class="hljs-string">"select-none"</span>&gt;
                            I agree to the Terms and Conditions
                        &lt;/label&gt;
                    &lt;/div&gt;


                    {<span class="hljs-comment">/* Submit */</span>}
                    &lt;button
                        <span class="hljs-keyword">type</span>=<span class="hljs-string">"submit"</span>
                        className=<span class="hljs-string">"w-full py-3 bg-red-600 hover:bg-red-700 text-white rounded font-semibold transition"</span>
                    &gt;
                        Sign Up
                    &lt;/button&gt;


                &lt;/form&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );
};
</code></pre>
<h3 id="heading-whats-wrong-with-the-form">What’s Wrong With The Form?</h3>
<ul>
<li><p>Lack of action feedback – no user feedback means that users can become confused as to whether an action has happened or not. No error messages or feedback offers the user no insight into what they need to do to correct the form.</p>
</li>
<li><p>No labels for form inputs – No labels for form inputs prevent screen readers from understanding their purpose. Some screen readers may miss placeholders, and once a user types within the input, the placeholder is replaced, losing context and making it hard to return to erroneous inputs.</p>
</li>
<li><p>Lack of accessibility markup to make the form optimised for screen readers and accessibility tools.</p>
</li>
</ul>
<p>So how do we make this better? Let’s jump right in.</p>
<h2 id="heading-error-handling-with-react-hook-form">Error Handling With React-Hook-Form</h2>
<p>Error handling on forms is a critical aspect of any form submission flow. Without it, the process becomes both chaotic and frustrating for the user. We can alleviate this frustration by adding some useful error messages which explain the issues.</p>
<p>A popular library for working with forms in React is the <code>react-hook-form</code> library. It’s used by over 1.4 million people according to their GitHub statistics.</p>
<p>Go ahead and install it if you don’t have it already:</p>
<pre><code class="lang-bash">npm install react-hook-form
</code></pre>
<p>We will then implement the basic required functions from the <code>react-hook-form</code> package, using the <code>useForm()</code> hook like so:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// define our type structure to use within the form</span>
<span class="hljs-keyword">type</span> FormData = {
    fullName: <span class="hljs-built_in">string</span>;
    email: <span class="hljs-built_in">string</span>;
    password: <span class="hljs-built_in">string</span>;
    confirmPassword: <span class="hljs-built_in">string</span>;
    agreeToTerms: <span class="hljs-built_in">boolean</span>;
};

<span class="hljs-comment">// basic usage of `useForm()`</span>
<span class="hljs-keyword">const</span> {
    register,
    handleSubmit,
    watch,
    formState: { errors },
  } = useForm&lt;Inputs&gt;()
</code></pre>
<p><strong>Quick Explanation:</strong></p>
<ul>
<li><p><code>register</code>: One of the key concepts in React Hook Form is to “register” your component / HTML element. This means you can access value of the element for both form validation and when submitting the form.</p>
</li>
<li><p><code>handleSubmit</code>: This is the key function needed to submit the form, run validation, and any other configured checks. It can take up to two arguments:</p>
<ol>
<li><p><code>handleSubmit(onSuccess)</code> – called when the submission of the form is valid and can submit ok.</p>
</li>
<li><p><code>handleSubmit(onSuccess, onFail)</code> – here you can pass the <code>handleSubmit()</code> method two functions: the first will be run when React Hook Form deems the form to be valid, and allows you to continue. The second will be called when the form sees an error. This could be from validation, or another stipulation.</p>
</li>
</ol>
</li>
<li><p><code>watch</code>: Watch is a function that monitors a specified element for changes and returns its value. For instance, if you’re watching an input element, you can output the user’s typing in real-time or have another element validate it against a predefined value. A good example is a confirm password matching the previous password field.</p>
</li>
<li><p><code>formState</code>: this is an object which holds information about your form. The <code>formState</code> object keeps track of the state of the form, like:</p>
<ol>
<li><p><strong>isDirty</strong> – <code>true</code> if the user has changed <em>any</em> input.</p>
</li>
<li><p><strong>isValid</strong> – <code>true</code> if the form passes all validations.</p>
</li>
<li><p><strong>errors</strong> – an object holding any validation errors per field.</p>
</li>
<li><p><strong>isSubmitting</strong> – <code>true</code> while the form is being submitted (useful for showing loading spinners)</p>
</li>
<li><p><strong>isSubmitted</strong> – <code>true</code> after the form has been submitted.</p>
</li>
<li><p><strong>touchedFields</strong> – which fields the user has interacted with.</p>
</li>
<li><p><strong>dirtyFields</strong> – which fields the user has modified.</p>
</li>
</ol>
</li>
</ul>
<p>We can use any of these properties by including them in our form state object. We are destructing the <code>errors</code> property so we can use the errors later in our form to either show error messages, or validate that there no errors on the page.</p>
<h2 id="heading-hooking-up-the-useform-methods-to-our-form">Hooking Up the <code>useForm</code> Methods to Our Form</h2>
<p>Now that we know more about the <code>useForm()</code> method and react-hook-form, we need to integrate this with our existing <code>&lt;form/&gt;</code> element. Doing so will allow us to use all the react-hook-form features we’ve discussed so far in our form.</p>
<pre><code class="lang-xml">import { TvIcon } from "@heroicons/react/24/outline";
import { useState } from "react";
import { useForm } from "react-hook-form";

type FormData = {
    fullName: string;
    email: string;
    password: string;
    confirmPassword: string;
    agreeToTerms: boolean;
};

export const RegistrationForm = () =&gt; {
    const {
        register,
        handleSubmit,
        formState: { errors },
        watch,
    } = useForm<span class="hljs-tag">&lt;<span class="hljs-name">FormData</span>&gt;</span>();

    const onSubmit = () =&gt; {
        alert(`Form submitted`);
    };

    return (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-center items-center w-screen h-screen bg-gray-900"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full max-w-md p-8 bg-black bg-opacity-75 rounded-lg"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-row justify-center items-center gap-x-4"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">TvIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-12 w-12 text-red-500"</span> /&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-7xl font-bold text-center text-white mb-4"</span>&gt;</span>Getflix<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl font-bold text-white mb-6 text-center"</span>&gt;</span>
                    Sign Up
                <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>


                <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleSubmit(onSubmit)}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"space-y-6"</span>&gt;</span>

                    {/* Full Name */}
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                            {<span class="hljs-attr">...register</span>("<span class="hljs-attr">fullName</span>", {
                                <span class="hljs-attr">required:</span> "<span class="hljs-attr">Full</span> <span class="hljs-attr">Name</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>"
                            })}
                            <span class="hljs-attr">aria-required</span>
                            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
                            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Full name"</span>
                            <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"</span>
                        /&gt;</span>
                        {errors.fullName &amp;&amp; (
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 text-sm mt-1"</span>&gt;</span>{errors.fullName.message}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                        )}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                    {/* Email */}
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                            {<span class="hljs-attr">...register</span>("<span class="hljs-attr">email</span>", {
                                <span class="hljs-attr">required:</span> "<span class="hljs-attr">Email</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>",
                                <span class="hljs-attr">pattern:</span> {
                                    <span class="hljs-attr">value:</span> /^\<span class="hljs-attr">S</span>+@\<span class="hljs-attr">S</span>+$/<span class="hljs-attr">i</span>,
                                    <span class="hljs-attr">message:</span> "<span class="hljs-attr">Invalid</span> <span class="hljs-attr">email</span> <span class="hljs-attr">address</span>",
                                },
                            })}
                            <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
                            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Email Address"</span>
                            <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"</span>
                        /&gt;</span>
                        {errors.email &amp;&amp; (
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 text-sm mt-1"</span>&gt;</span>{errors.email.message}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                        )}

                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                    {/* Password */}
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                            {<span class="hljs-attr">...register</span>("<span class="hljs-attr">password</span>", {
                                <span class="hljs-attr">required:</span> "<span class="hljs-attr">Please</span> <span class="hljs-attr">enter</span> <span class="hljs-attr">your</span> <span class="hljs-attr">password</span>",
                            })}
                            <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span>
                            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Password"</span>
                            <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"</span>
                        /&gt;</span>
                        {errors.password &amp;&amp; (
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 text-sm mt-1"</span>&gt;</span>{errors.password.message}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                        )}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                    {/* Confirm Password */}
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                            {<span class="hljs-attr">...register</span>("<span class="hljs-attr">confirmPassword</span>", {
                                <span class="hljs-attr">required:</span> "<span class="hljs-attr">Please</span> <span class="hljs-attr">enter</span> <span class="hljs-attr">your</span> <span class="hljs-attr">password</span>",
                                <span class="hljs-attr">validate:</span> (<span class="hljs-attr">value</span>) =&gt;</span>
                                    value === watch("password") || "Passwords do not match",
                            })}
                            type="password"
                            placeholder="Confirm Password"
                            className="w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"
                        /&gt;
                        {errors.confirmPassword &amp;&amp; (
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 text-sm mt-1"</span>&gt;</span>{errors.confirmPassword.message}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                        )}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                    {/* Agree to Terms */}
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center text-gray-400 text-sm"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                            {<span class="hljs-attr">...register</span>("<span class="hljs-attr">agreeToTerms</span>", {
                                <span class="hljs-attr">required:</span> "<span class="hljs-attr">You</span> <span class="hljs-attr">must</span> <span class="hljs-attr">agree</span> <span class="hljs-attr">to</span> <span class="hljs-attr">the</span> <span class="hljs-attr">terms</span> <span class="hljs-attr">and</span> <span class="hljs-attr">conditions</span>"
                            })}
                            <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span>
                            <span class="hljs-attr">id</span>=<span class="hljs-string">"agreeToTerms"</span>
                            <span class="hljs-attr">className</span>=<span class="hljs-string">"mr-2"</span>
                        /&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"select-none"</span>&gt;</span>
                            I agree to the Terms and Conditions
                        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>

                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    {errors.agreeToTerms &amp;&amp; (
                        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 text-sm mt-1"</span>&gt;</span>{errors.agreeToTerms.message}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                    )}


                    {/* Submit */}
                    <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                        <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
                        <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full py-3 bg-red-600 hover:bg-red-700 text-white rounded font-semibold transition"</span>
                    &gt;</span>
                        Sign Up
                    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

                    {/* Already have account */}
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center text-gray-400 text-sm mt-4"</span>&gt;</span>
                        Already have an account?{" "}
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 hover:underline"</span>&gt;</span>
                            Sign In
                        <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span> &gt;</span>
    );
};
</code></pre>
<p>So in the updated form code, we’ve made a few adjustments:</p>
<h3 id="heading-registered-each-our-elements">Registered Each Our Elements</h3>
<p>For each of our elements we’ve added the <code>register</code> object, and configuring some overrides.</p>
<p>We added the <strong>required</strong> property to all input fields, which checks if the element has a value. If not, it records the provided name and marks the error as erroneous, updating the errors object with our name and the provided <em>required</em> message.</p>
<pre><code class="lang-typescript"> {...register(<span class="hljs-string">"fullName"</span>, {
    required: <span class="hljs-string">"Full Name is required"</span>
  })}
</code></pre>
<p>We’ve added a <code>pattern</code> property on the email’s register object. This allows us to specify a criteria for the value of the input – perfect for passwords, email fields, and other inputs which may have value restrictions, or requirements.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// valid email pattern</span>
pattern: {
    value: <span class="hljs-regexp">/^\S+@\S+$/i</span>,
    message: <span class="hljs-string">"Invalid email address"</span>,
},
</code></pre>
<p>We have also added the <code>validate</code> property to the confirm password element. This is a given function that will run as the user types.</p>
<pre><code class="lang-typescript">validate: <span class="hljs-function">(<span class="hljs-params">value</span>) =&gt;</span> value === watch(<span class="hljs-string">"password"</span>) || <span class="hljs-string">"Passwords do not match"</span>
</code></pre>
<p>The <code>validate</code> function inside <code>register</code> is run <strong>automatically</strong> based on the field's <code>validationMode</code> setting.</p>
<p>By default (if you do not specify the <code>validationMode</code>), React Hook Form runs validation on <code>onChange</code> and <code>onBlur</code> events. This means that:</p>
<ul>
<li><p>When the user types into the input → it triggers <code>validate</code>.</p>
</li>
<li><p>When the user leaves (blurs) the input → it triggers <code>validate</code> again.</p>
</li>
</ul>
<p>If you wanted to update the custom validation mode, you can override this using the <code>mode</code> setting within <code>useForm()</code> like so:</p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">const</span> { register, handleSubmit, formState, trigger } = useForm({
    mode: <span class="hljs-string">"onSubmit"</span>,
  });
</code></pre>
<p>If you then want to go an extra step and update the mode per element, overriding the <code>mode</code> setting you just globally set for your form, you can use the <code>trigger()</code> method from <code>useForm</code> like so:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span>
  {<span class="hljs-attr">...register</span>("<span class="hljs-attr">email</span>", { <span class="hljs-attr">required:</span> "<span class="hljs-attr">Email</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>" })}
  <span class="hljs-attr">onBlur</span>=<span class="hljs-string">{()</span> =&gt;</span> trigger("email")} // validate this field onBlur manually
/&gt;
</code></pre>
<p>This allows you to have <code>onSubmit</code> validation set via <code>mode</code>, and then email is triggered via <code>onBlur()</code> too.</p>
<p>Just adding these simple settings within the react-hook-form library already gives us a much better user experience than before – but it isn’t everything. Let’s explore more settings, HTML, and attributes we can add to increase accessibility and user experience.</p>
<h2 id="heading-showing-error-messages">Showing Error Messages</h2>
<p>Form errors can be stored within the <code>formState</code> object we mentioned earlier, but they’re no good there – we need to display them to our users. We can achieve this simply by accessing the destructed <code>errors</code> object, like below:</p>
<pre><code class="lang-xml">{errors.password &amp;&amp; (
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 text-sm mt-1"</span>&gt;</span>{errors.password.message}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
)}
</code></pre>
<p>The code uses conditional syntax to show the <code>&lt;p&gt;</code> tag only if the <code>errors.password</code> object has a value, indicating an error associated with the password field from <code>useForm()</code> checks. We can then display the error message from <code>errors.password.message</code>, combined with a commonly used erroneous colour like red, to highlight the form’s problems. This can then been applied to all other input fields as per the code above.</p>
<h2 id="heading-adding-aria-required">Adding <code>aria-required</code></h2>
<p>So we’ve informed the form that certain elements are required and these should be checked when submitting the form. But this alone doesn’t inform visually impaired users that the element is required.</p>
<p>To aid with screen-readers, we can add an <code>aria</code> attribute to our element which will be read by the screen-reader. This property is the <code>aria-required</code> property. This means that when the screen-reader reads out information about the element it will inform the user that this value is required for successful submission.</p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
    {<span class="hljs-attr">...register</span>("<span class="hljs-attr">fullName</span>", {
        <span class="hljs-attr">required:</span> "<span class="hljs-attr">Full</span> <span class="hljs-attr">Name</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>"
    })}
    <span class="hljs-attr">aria-required</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Full name"</span>
    <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"</span>
/&gt;</span>
</code></pre>
<h2 id="heading-adding-fieldset-and-legend">Adding <code>fieldset</code> and <code>legend</code></h2>
<p>Fieldset elements group <code>&lt;form&gt;</code> controls together, while legend elements provide a description for the grouped controls.</p>
<p>Imagine you have one big form, but it spans two "sections" – for example, a "<em>User Details</em>" section for username, email, and passwords, and an "<em>Address Details</em>" section asking for your shipping and billing information.</p>
<p>In this tutorial, we’re using TailwindCSS, which provides a utility class called <code>sr-only</code>. You can apply <code>sr-only</code> to your legends so they are only visible to screen readers, and not actually visible on the page.</p>
<p>This way, the legend will be read aloud when users navigate into a section of the form, making it clear which part of the form they are interacting with.</p>
<p><strong>Important Note:</strong> Legends must be placed inside fieldsets. You need to wrap your legends within a <code>&lt;fieldset&gt;</code> element for your HTML to be valid and accessible.</p>
<p>Here's an unrelated example (to keep it brief and simple):</p>
<pre><code class="lang-xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">fieldset</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">legend</span>&gt;</span>Payment Method<span class="hljs-tag">&lt;/<span class="hljs-name">legend</span>&gt;</span>    
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"radio"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"payment"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"card"</span> /&gt;</span>
      Credit Card
    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"radio"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"payment"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"paypal"</span> /&gt;</span>
      PayPal
    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">fieldset</span>&gt;</span>
</code></pre>
<p>You can see that the payment option inputs have been grouped within a fieldset, and then described by the <code>legend</code> element, informing the user that these elements relate to “<em>Payment Method</em>”. You as the developer can then decide if you would like this shown to everyone, or if it’s only for visually impaired users.</p>
<p>For screen readers, they’d hear something like:</p>
<blockquote>
<p>"Group: Payment Method. Credit Card radio button. PayPal radio button."</p>
</blockquote>
<h2 id="heading-do-not-rely-on-placeholders-only">Do Not Rely on Placeholders Only!</h2>
<p>Placeholders are a great addition to make it clear to the user what the input elements are used for, and show helpful information. But they aren’t that user friendly, especially in regards to screen-readers.</p>
<p>The main reasons for this are:</p>
<ul>
<li><p>Placeholders disappear when typing, meaning that if a user begins to type “<em>Grant</em>”, and then tabs away from the input when they go back, without a label it will simply read the value of the input, not what it relates to.</p>
</li>
<li><p>Often developers utilise a grey-like colour for their placeholders, with a low opacity. This can mean it’s difficult for users to sometimes see the placeholder, especially those who are colour blind or visually impaired.</p>
</li>
</ul>
<p>So what can we do instead ? Well this leads me onto our next point – we can use a common HTML element, the <code>&lt;label/&gt;</code>.</p>
<h2 id="heading-adding-labels-and-using-htmlfor">Adding Labels and Using <code>htmlFor</code></h2>
<p>Another accessibility feature we can add to boost our accessibility and user experience for all, is the <code>htmlFor</code> attribute combined with the <code>&lt;label/&gt;</code> element.</p>
<p>Labels are highly important for both sighted and visually impaired users. It offers clarity as to what the input is associated with, as well as a navigational tool for those using screen-readers.</p>
<p>The <code>htmlFor</code> attribute is used to link <code>&lt;label/&gt;</code> elements with their input.</p>
<p><strong>Note:</strong> <code>htmlFor</code> <em>attributes can only be used on labels and are not valid on any other element.</em></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"fullname"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-white"</span>&gt;</span>Full Name<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">input</span>
    {<span class="hljs-attr">...register</span>("<span class="hljs-attr">fullName</span>", {
        <span class="hljs-attr">required:</span> "<span class="hljs-attr">Full</span> <span class="hljs-attr">Name</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>"
    })}
    <span class="hljs-attr">id</span>=<span class="hljs-string">"fullname"</span>
    <span class="hljs-attr">aria-required</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Full name"</span>
    <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"</span>
/&gt;</span>
</code></pre>
<p>Why this is important for accessibility:</p>
<h4 id="heading-1-screen-readers">1. Screen readers:</h4>
<p>When a screen reader lands on the <code>&lt;input&gt;</code>, it automatically reads the associated label ("Full Name"). Even if the label is not visually right next to the input, the screen reader still knows which text describes the input, giving you some freedom when designing your forms.</p>
<h4 id="heading-2-click-behaviour">2. Click behaviour:</h4>
<p>When you click the <code>&lt;label&gt;</code>, it automatically focuses the <code>&lt;input&gt;</code> when using <code>htmlFor</code>.</p>
<p>Users don’t have to click exactly on the tiny input field – and this can certainly be useful when dealing with checkboxes or radio buttons, for example.</p>
<p>In short, big click targets = better usability and faster form filling.</p>
<p>This is also very helpful for mobile users where precision tapping is hard, especially on smaller screens.</p>
<h2 id="heading-give-additional-information-with-aria-describedby"><strong>Give Additional Information With</strong> <code>aria-describedBy</code></h2>
<p>Now that we’ve added clear labels to our form fields, we can take accessibility a step further by providing additional guidance for users when errors occur. By using <code>aria-describedby</code> and <code>aria-invalid</code>, we can link helpful error messages to the input fields and ensure screen readers communicate validation issues clearly. Let’s look at how to implement this:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-white"</span>&gt;</span>Email<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
    {<span class="hljs-attr">...register</span>("<span class="hljs-attr">email</span>", {
          <span class="hljs-attr">required:</span> "<span class="hljs-attr">You</span> <span class="hljs-attr">must</span> <span class="hljs-attr">enter</span> <span class="hljs-attr">an</span> <span class="hljs-attr">email</span> <span class="hljs-attr">address</span>",
      <span class="hljs-attr">pattern:</span> {
        <span class="hljs-attr">value:</span> /^\<span class="hljs-attr">S</span>+@\<span class="hljs-attr">S</span>+$/<span class="hljs-attr">i</span>,
        <span class="hljs-attr">message:</span> "<span class="hljs-attr">Invalid</span> <span class="hljs-attr">email</span> <span class="hljs-attr">address</span>",
      },
    })}
    <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
    <span class="hljs-attr">aria-invalid</span>=<span class="hljs-string">{errors.email</span> ? "<span class="hljs-attr">true</span>" <span class="hljs-attr">:</span> "<span class="hljs-attr">false</span>"}
    <span class="hljs-attr">aria-describedby</span>=<span class="hljs-string">{errors.email</span> ? "<span class="hljs-attr">email-error</span>" <span class="hljs-attr">:</span> <span class="hljs-attr">undefined</span>}
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter your email address"</span>
    <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"</span>
  /&gt;</span>
  {errors.email &amp;&amp; (
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"email-error"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 text-sm mt-1"</span>&gt;</span>
      {errors.email.message}
    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  )}
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Notice the two new attributes we’ve added:</p>
<ul>
<li><p><code>aria-describedBy</code> – this attribute links our error message with our input. Screen readers will therefore read out the error message whilst reading out other information when the input is focused.</p>
</li>
<li><p><code>aria-invalid</code> – this attribute again aids with screen readers, informing the user that the input’s value is invalid and they need to correct it. This combined with the <code>describedBy</code> attribute gives visually impaired users all the information they need in order to correct their mistake.</p>
</li>
</ul>
<h2 id="heading-avoid-tooltips-for-critical-information">Avoid Tooltips for Critical Information</h2>
<p>When developing your form, try to avoid tooltips (those little elements that show when you hover over another element for a period of time like below).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745685939025/ce427cf1-ef44-4021-b8d3-0095e7a091c6.png" alt="Example of a tooltip showing text that appears when a user hovers over a term." class="image--center mx-auto" width="824" height="228" loading="lazy"></p>
<p>The problems with using tooltips are:</p>
<ol>
<li><p>They often require <strong>mouse hover</strong>, which doesn't work on touch devices (for example mobile phones, or tablets).</p>
</li>
<li><p>They aren’t announced reliably by screen readers if proper aria labels aren’t added.</p>
</li>
<li><p>They disappear too quickly</p>
</li>
</ol>
<p>Instead, we can use inline helper text or descriptions combined with <code>aria-describedby</code> like below:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"passwordHint"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xs text-gray-500"</span>&gt;</span>
  Must be at least 8 characters and include a number.
<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>We can then reference this within our input using the <code>aria-describedBy</code> attribute. But wait, we already have a <code>describedBy</code> pointing at the error message – well, that’s ok! We can link multiple elements, like the brief example below:</p>
<pre><code class="lang-xml">// now references both passwordHint and the password error (we seperate the ids with a space)
<span class="hljs-tag">&lt;<span class="hljs-name">input</span> 
  <span class="hljs-attr">id</span>=<span class="hljs-string">"password"</span>
  <span class="hljs-attr">aria-describedby</span>=<span class="hljs-string">"passwordHint passwordError"</span>
/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"passwordHint"</span>&gt;</span>
  Must be at least 8 characters long.
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"passwordError"</span>&gt;</span>
  Passwords do not match!
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h2 id="heading-tell-me-something-important">Tell Me Something Important</h2>
<p><code>aria-live</code> is an aria attribute you can add to an element to tell screen readers:</p>
<blockquote>
<p>"Hey, if the content inside me changes, announce it automatically."</p>
</blockquote>
<p>It makes dynamic content updates audible without needing the user to re-focus anything.</p>
<p>A basic example could look something like below, where a message which is updated upon submission is updated, it could contain something like:</p>
<blockquote>
<p>“Loading” → “<em>Hurray, registration complete”</em></p>
<p>or</p>
<p>““Pending” → “Registration failed due to many errors”</p>
</blockquote>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">aria-live</span>=<span class="hljs-string">"polite"</span>&gt;</span>
  {formSubmissionResultMessage}
<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>When <code>formSubmissionResultMessage</code> changes, screen readers will automatically announce the updated message.</p>
<p>The timing of when it is read out depends on the value of the <code>aria-live</code> attribute – with <code>polite</code>, the announcement waits for a natural pause. With <code>assertive</code>, it interrupts immediately.</p>
<h3 id="heading-real-world-examples">Real-World Examples</h3>
<h4 id="heading-polite-update-good-for-passive-notifications">Polite update: good for passive notifications</h4>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">aria-live</span>=<span class="hljs-string">"polite"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-2 text-green-500"</span>&gt;</span>
  Form saved successfully.
<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>The screen reader waits for a good moment to say it.</p>
<h4 id="heading-assertive-update-good-for-urgent-errors">Assertive update: good for urgent errors</h4>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">aria-live</span>=<span class="hljs-string">"assertive"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-2 text-red-500"</span>&gt;</span>
  Passwords do not match!
<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>The screen reader <strong>immediately</strong> interrupts and announces it.</p>
<h4 id="heading-good-things-to-know">Good things to know:</h4>
<ul>
<li><p>The element needs to <strong>already exist</strong> in the DOM when the update happens. So it’s smart to always render the <code>&lt;p aria-live&gt;</code> – just update its content.</p>
</li>
<li><p>Don’t overuse <code>assertive</code>, or you’ll annoy users and make apps feel super noisy and overwhelming.</p>
</li>
</ul>
<h2 id="heading-focus-states-and-colouring">Focus States and Colouring</h2>
<p>You may have noticed on the input elements that I have added some custom colouring with TailwindCSS classes <code>focus:</code>. But what is this doing?</p>
<p>Well, this allows us to control the focus colour of the inputs. Without this, the browser will apply its own default styling which may not be as accessible to our users, especially those with colour-blindness.</p>
<p>For example, within our form, without the styling the input with focus looks like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745687500364/4bfa9c86-d908-4f2b-a674-485a1ed15bc3.png" alt="image of the form with a faint white and blue outline around the focussed input" class="image--center mx-auto" width="514" height="673" loading="lazy"></p>
<p>Here you can see it has applied a subtle white and blue outline – but its not that clear it’s being focused. You can argue it is different enough to other input elements, but for some users this may not be enough.</p>
<p>To combat this and improve usability, we can override this with our own custom colouring. When using TailwindCSS, we can apply the following class names:</p>
<pre><code class="lang-xml">focus:outline-none focus:ring-2 focus:ring-red-500
</code></pre>
<h3 id="heading-what-does-this-do">What Does This Do?</h3>
<p>This now applies a much thicker red line (encompassing brand colours) as well as making it clearer against the darker background</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Class name</strong></td><td><strong>Meaning (CSS equivalent)</strong></td></tr>
</thead>
<tbody>
<tr>
<td><code>focus:outline-none</code></td><td>Remove the outline when the element is focused</td></tr>
<tr>
<td><code>focus:ring-2</code></td><td>On focus, apply a <strong>2px wide ring</strong> (like a border/shadow)</td></tr>
<tr>
<td><code>focus:ring-red-500</code></td><td>Set the ring colour to Tailwind’s <code>red-500</code> colour</td></tr>
</tbody>
</table>
</div><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745687470893/c003c194-5f93-491d-a16e-4d983ea557ac.png" alt="image of form with thick red outline around the focussed input" class="image--center mx-auto" width="537" height="677" loading="lazy"></p>
<p>If you’re not using TailwindCSS, you can accomplish the same with plain CSS like so:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:focus</span> {
  <span class="hljs-attribute">outline</span>: none; <span class="hljs-comment">/* no default browser outline */</span>
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">2px</span> <span class="hljs-number">#ef4444</span>; <span class="hljs-comment">/* 2px red ring around input */</span>
}
</code></pre>
<h2 id="heading-make-buttons-descriptive">Make Buttons Descriptive</h2>
<p>A super simple way to level up your form’s user experience is to make sure your buttons use clear, descriptive text.</p>
<p>Let’s take a look at a few examples of buttons that don’t quite achieve this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745688682031/54a84908-e0fd-4781-ab83-6c34ab360cae.png" alt="image showing different poor input buttons" class="image--center mx-auto" width="545" height="336" loading="lazy"></p>
<p>The above buttons are examples of poor input buttons because:</p>
<ul>
<li><p>“Click Here” doesn’t give any context. Screen reader users, and even sighted users, have no idea what "click here" does without reading nearby text.</p>
</li>
<li><p>Icon Only: Sighted users <em>might</em> guess what the icon means, but screen readers see nothing unless you add <code>aria-label</code>. The point is, it is ambiguous and unclear as to what the button does. You may see websites that just use an icon, not surrounded by a button, which can be even more confusing.</p>
</li>
<li><p>“Submit”: If you have several "Submit" buttons (for example, one for payment, one for contact form), users don't know which "submit" is doing what.</p>
</li>
</ul>
<h3 id="heading-improvements">Improvements</h3>
<p>Instead, we can improve those buttons to be more accessible and user-friendly by doing the following:</p>
<ul>
<li><p><strong>Use descriptive button text</strong> – for example: "Pay Now", "Sign Up", or "Save Changes".</p>
</li>
<li><p><strong>Use both an icon and text</strong> – combining an icon with text can be the perfect blend for both accessibility and design.</p>
</li>
<li><p><strong>Use</strong> <code>aria-label</code> – if you really must use an icon-only button (like a basket or home icon in a navigation bar), make sure to add an <code>aria-label</code> attribute to clearly describe the button’s purpose, like so:</p>
</li>
</ul>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
    <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
    <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full py-3 px-6 rounded-lg bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 text-white text-lg font-semibold transition"</span>
&gt;</span> Pay Now <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">button</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
    <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full py-3 px-6 rounded-lg bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 text-white text-lg font-semibold flex justify-center items-center gap-2 transition"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">HomeIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-6 w-6"</span> /&gt;</span>
        Home
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">button</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
    <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Go to homepage"</span>
    <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full py-3 px-6 rounded-lg bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 text-white text-lg font-semibold flex justify-center items-center transition"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">HomeIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-6 w-6"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>That code would generate the following:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745787493605/e5259e87-f8ee-463f-bc57-662779eea698.png" alt="image showing more accessible buttons from above html" class="image--center mx-auto" width="513" height="326" loading="lazy"></p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>In this tutorial, we’ve covered various ways to make your forms more accessible and user-friendly. From simple things like making button text clearer and using more user-friendly colours, to more complex HTML attributes like <code>aria-describedBy</code> and <code>aria-live</code>, you should be covered.</p>
<p>I hope you found this tutorial helpful, and now you’re ready to take your development skills to the next level. Making these simple changes can have a big impact on your users’ experience, and they’ll definitely stick around longer and be less frustrated.</p>
<p>As always, if you’d like to share feedback on the article, discuss it further, or just hear about future articles or content, you can drop me a follow on X (Twitter) via my handle <a target="_blank" href="https://x.com/grantdotdev">@grantdotdev</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use TestContainers in .Net ]]>
                </title>
                <description>
                    <![CDATA[ At some point in your development lifecycle, you will need to test that your system can integrate with another system, whether it be another API, a database, or caching service, for example. This can be a laborious task of spinning up other servers h... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-testcontainers-in-net/</link>
                <guid isPermaLink="false">67e2cbfdaa97659cd53cf39f</guid>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testcontainers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Integration Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Tue, 25 Mar 2025 15:30:05 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742773343798/44c64acc-3862-4325-af21-6b7de417d300.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>At some point in your development lifecycle, you will need to test that your system can integrate with another system, whether it be another API, a database, or caching service, for example. This can be a laborious task of spinning up other servers hosting the 3rd party API replica, or permanently hosting a SQL database seeded with test data.</p>
<p>In this article, I’ll teach you how to use the TestContainers library to make running integration tests much easier and more manageable.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-testcontainers">What Is TestContainers?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-does-it-all-work">How Does It All Work?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-your-first-test">How to Set Up Your First Test</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-key-behaviors-of-iasynclifetime-in-a-test-class">Key Behaviors of IAsyncLifetime in a Test Class</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-improve-performance">How to Improve Performance</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-explanation-of-differences">Explanation of Differences</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-share-your-container-across-multiple-test-classes">How to Share Your Container Across Multiple Test Classes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary-of-approaches">Summary of Approaches:</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-multiple-containers">How to Create Multiple Containers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-make-your-setup-easier-with-custom-images">How to Make Your Setup Easier With Custom Images</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Understanding of Docker</p>
</li>
<li><p>Understanding of xUnit and testing</p>
</li>
<li><p>Installation of the following packages:</p>
<ul>
<li><p><code>TestContainers</code></p>
</li>
<li><p><code>TestContainers.MsSql</code></p>
</li>
<li><p>xUnit</p>
</li>
<li><p>&gt;= .Net 8</p>
</li>
<li><p><code>FluentAssertions</code></p>
</li>
<li><p><code>Microsoft.Data.SqlClient</code></p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-what-is-testcontainers">What Is TestContainers?</h2>
<p><a target="_blank" href="https://testcontainers.com">TestContainers</a> is an open source library that provides you with easily disposable container instances for things like database hosting, message brokers, browsers and more – basically anything that can run in a Docker container.</p>
<p>It removes the necessity to maintain hosted environments for testing in the cloud or on local machines. As long as the user’s machine and CI/CD host supports Docker, the testContainer tests can easily be run.</p>
<h2 id="heading-how-does-it-all-work">How Does It All Work?</h2>
<p>You define the image you’re wanting to utilise, and specify a configuration.</p>
<p>The TestContainer library spins up a Docker Container with the configured image.</p>
<h3 id="heading-provides-connection-details"><strong>Provides Connection Details</strong></h3>
<p>After starting the container, TestContainers exposes connection strings (for example, a database connection URL), so your tests can use the real service, rather than having to configure this yourself.</p>
<h3 id="heading-cleans-up-automatically"><strong>Cleans Up Automatically</strong></h3>
<p>When the test finishes, TestContainers removes the container automatically, ensuring no leftover resources. This is one of the best things about using TestContainers: all the creation, tear down, and container setup is handled within the library itself, making it perfect for use within delivery pipelines.</p>
<h2 id="heading-how-to-set-up-your-first-test">How to Set Up Your First Test</h2>
<p>For the purpose of this tutorial, we’re going to keep things simple, and only use a <code>MS Sql Server</code> image.</p>
<p>The first thing we’re going to do is configure our Microsoft SQL Server Docker container via the TestContainer fluid API.</p>
<p>Create your test class like below:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">IntegrationTests</span>: <span class="hljs-title">IAsyncLifetime</span> 
{
    <span class="hljs-keyword">private</span> MsSqlContainer _container;
    <span class="hljs-keyword">private</span> FakeLogger _<span class="hljs-function">logger

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">InitializeAsync</span>(<span class="hljs-params"></span>)</span>
    {
           _container = <span class="hljs-keyword">new</span> MsSqlBuilder()
                .WithImage(<span class="hljs-string">"mcr.microsoft.com/mssql/server:2022-latest"</span>)
                .WithPassword(<span class="hljs-string">"P@ssw0rd123"</span>)
                .WithPortBinding(<span class="hljs-number">1443</span>)
                .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(<span class="hljs-number">1433</span>))
                .Build();

            _logger = <span class="hljs-keyword">new</span> FakeLogger();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">DisposeAsync</span>(<span class="hljs-params"></span>)</span> =&gt; <span class="hljs-keyword">await</span> _container.DisposeAsync();
}
</code></pre>
<p>Here we’re using xUnit’s <code>IAsyncLifetime</code> interface. It’s an interface in xUnit that provides a way to handle async setup and teardown for test classes. It's useful when you need to initialise and clean up resources asynchronously. We’re using the <code>InitializeAsync()</code> to setup and define our Microsoft SQL Database container as well as starting the container, then using the <code>DisposeAsync()</code> method to stop and dispose of our container.</p>
<h3 id="heading-explanation-of-builder-methods">Explanation of Builder Methods</h3>
<ul>
<li><p><code>WithImage()</code>: this allows us to specify the image we want Docker to pull down and run. We’ve opted for the latest version of SQL Server 2022.</p>
</li>
<li><p><code>WithPassword()</code>: This allows us to specify the password for the database (when creating most databases, a password is normally required).</p>
</li>
<li><p><code>WithPortBinding()</code>: This allows us to specify both the hosting port number on your machine, as well as the container port number</p>
</li>
<li><p><code>WithWaitStrategy()</code>: Here we can specify a wait strategy, which informs our container to wait for a condition before the container is ready to use. This is important because some services (like databases or APIs) take time to fully start up.</p>
</li>
<li><p><code>Build()</code>" This is the command that builds the test container based on the configuration. This <strong>does not</strong> run or start the container – you can do this using the <code>container.StartAsync()</code> method as mentioned previously.</p>
</li>
</ul>
<h4 id="heading-why-is-withwaitstrategy-needed"><strong>Why Is</strong> <code>WithWaitStrategy()</code> Needed?</h4>
<p>By default, TestContainers assumes the container is ready as soon as it starts running. But some services might:</p>
<ul>
<li><p>Take time to initialize.</p>
</li>
<li><p>Require a specific log message before they are ready.</p>
</li>
<li><p>Need a port to be accessible before you can connect.</p>
</li>
</ul>
<p>Using <code>WithWaitStrategy()</code>, you can customise how TestContainers waits before considering the container "ready."</p>
<h3 id="heading-adding-the-test">Adding the Test</h3>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">IntegrationTests</span>: <span class="hljs-title">IAsyncLifetime</span> 
{
    <span class="hljs-keyword">private</span> MsSqlContainer _container;
    <span class="hljs-keyword">private</span> FakeLoger _logger;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">InitializeAsync</span>(<span class="hljs-params"></span>)</span>
    {
           _container = <span class="hljs-keyword">new</span> MsSqlBuilder()
                .WithImage(<span class="hljs-string">"mcr.microsoft.com/mssql/server:2022-latest"</span>)
                .WithPassword(<span class="hljs-string">"P@ssw0rd123"</span>)
                .WithPortBinding(<span class="hljs-number">1443</span>)
                .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(<span class="hljs-number">1433</span>))
                .Build();

            <span class="hljs-keyword">await</span> _container.StartAsync();
            _logger = <span class="hljs-keyword">new</span> FakeLogger();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">DisposeAsync</span>(<span class="hljs-params"></span>)</span> =&gt; <span class="hljs-keyword">await</span> _container.DisposeAsync();

    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">Test_Database_Connection</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">var</span> connectionString = _container.GetConnectionString();
        <span class="hljs-keyword">using</span> <span class="hljs-keyword">var</span> conn = <span class="hljs-keyword">new</span> SqlConnection(connectionString);
        <span class="hljs-keyword">await</span> conn.OpenAsync();

        Assert.True(conn.State == System.Data.ConnectionState.Open);
    }
}
</code></pre>
<p>The above test, although it’s simple, illustrates how easy it is to spin up a container and create a simple test. The above test will work, but it can lead to low performing tests and high usage of machine resource when not used correctly. Let me explain:</p>
<p>Using <code>IAsyncLifetime</code> is necessary, as we’re calling async setup methods (<code>StartAsync</code>), for example. But the <code>InitializeAsync() / DisposeAsync()</code> methods when situated in a test class are run before and after every test (<code>Fact</code> in xUnit).</p>
<p>This means that every time a test begins, it is:</p>
<ul>
<li><p>creating a brand new Docker container,</p>
</li>
<li><p>pulling the MS Sql image,</p>
</li>
<li><p>creating the DB,</p>
</li>
<li><p>running the tests, and</p>
</li>
<li><p>tearing down the container.</p>
</li>
</ul>
<p>You can test this by copying and pasting the above <code>Test_Database_Connection()</code> test multiple times, adding a number to each duplicate test (to keep the compiler happy), and opening Docker Desktop. Running all the tests, you will see a new container (with a different name) being created for each test run.</p>
<p>Now, this can be acceptable if you have a limited number of tests in your test class. But it can have negative outcomes on test classes with a larger number of tests, meaning test maintenance and planning is key. It’s useful, though, when you want to make sure that the database is in a completely clean state before each test, ensuring no data contamination from other tests running.</p>
<h2 id="heading-key-behaviors-of-iasynclifetime-in-a-test-class"><strong>Key Behaviors of</strong> <code>IAsyncLifetime</code> in a Test Class</h2>
<p>When your test class implements <code>IAsyncLifetime</code>, xUnit's default behaviour is:</p>
<p>1. Creates a new instance of the test class for each test method.<br>2. Calls <code>InitializeAsync()</code> before each test.<br>3. Calls <code>DisposeAsync()</code> after each test.</p>
<h3 id="heading-what-does-this-mean-for-testcontainers"><strong>What Does This Mean for TestContainers?</strong></h3>
<ul>
<li><p>In our case, since <code>InitializeAsync()</code> sets up a new container, a new container is created for each test.</p>
</li>
<li><p><code>DisposeAsync()</code> stops the container after each test finishes.</p>
</li>
<li><p>Ensures a completely fresh database state for every test, avoiding data contamination.</p>
</li>
<li><p>Is slow and resource-intensive, especially if you have many test methods.</p>
</li>
</ul>
<p>A more visual look on a test class could look like this:</p>
<p>🟢 InitializeAsync() -&gt; New Container Created (For Test_1)</p>
<p>🧪 Running Test_1</p>
<p><strong>🛑</strong> DisposeAsync() -&gt; Container Stopped (After Test_1)</p>
<p>🟢 InitializeAsync() -&gt; New Container Created (For Test_2)</p>
<p>🧪 Running Test_2</p>
<p><strong>🛑</strong> DisposeAsync() -&gt; Container Stopped (After Test_2)</p>
<h3 id="heading-when-is-this-useful"><strong>When Is This Useful?</strong></h3>
<ul>
<li><p>You need a completely fresh database state or container for each test.</p>
</li>
<li><p>Avoids test data contamination.</p>
</li>
<li><p>Each test starts from a clean slate.</p>
</li>
</ul>
<h3 id="heading-when-is-this-a-problem"><strong>When Is This a Problem?</strong></h3>
<ul>
<li><p>It results in slow execution – a new container is started for every test.</p>
</li>
<li><p>It’s resource-heavy – multiple containers run sequentially.</p>
</li>
<li><p>And it’s not scalable – hundreds of tests will take a long time to complete.</p>
</li>
</ul>
<h2 id="heading-how-to-improve-performance">How to Improve Performance</h2>
<p>Ok, so we’ve seen how to create containers once per test, and explored scenarios where this would be useful, but what if performance and cost are a concern?</p>
<p>Here we can combine <code>IClassFixture</code> and <code>IAsyncLiftetime</code> to achieve a <em>Once per test class</em> approach, where we create one container and one database, and its lifecycle is the full length of the test class (that is, all tests run against the same DB).</p>
<h3 id="heading-how-to-write-this">How to Write This</h3>
<p>We can utilise a TestFixture class which inherits the IAsyncLifetime interface, exposing the <code>InitializeAsync()</code> and <code>DisposeAsync()</code> methods as before.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> DotNet.Testcontainers.Builders;
<span class="hljs-keyword">using</span> Microsoft.Extensions.Logging.Testing;
<span class="hljs-keyword">using</span> Testcontainers.MsSql;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">IntegrationTests</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">TestClassFixture</span> : <span class="hljs-title">IAsyncLifetime</span>
{
    <span class="hljs-keyword">public</span> MsSqlContainer Container { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">private</span> FakeLogger _logger;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">InitializeAsync</span>(<span class="hljs-params"></span>)</span>
    {
        Container = <span class="hljs-keyword">new</span> MsSqlBuilder()
            .WithImage(<span class="hljs-string">"mcr.microsoft.com/mssql/server:2022-latest"</span>)
            .WithPassword(<span class="hljs-string">"P@ssw0rd123"</span>)
            .WithPortBinding(<span class="hljs-number">1443</span>)
            .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(<span class="hljs-number">1433</span>))
            .Build();

        _logger = <span class="hljs-keyword">new</span> FakeLogger();
        <span class="hljs-keyword">await</span> Container.StartAsync();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">DisposeAsync</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">await</span> Container.DisposeAsync();
    }
}
</code></pre>
<p>Using xUnit’s <code>IClassFixture</code> interface, we can pass our <code>TestClassFixture</code> and have our test class inherit from this. A test fixture is only run once per test class, making it perfect for our scenario.</p>
<pre><code class="lang-csharp">
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">IntegrationFixtureTests</span> : <span class="hljs-title">IClassFixture</span>&lt;<span class="hljs-title">TestClassFixture</span>&gt;
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">string</span> _connectionString;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">IntegrationFixtureTests</span>(<span class="hljs-params">TestClassFixture testClassFixture</span>)</span>
    {
        _connectionString = testClassFixture.Container.GetConnectionString();

        <span class="hljs-comment">// other test class specific setup goes here</span>
    }

    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">Test_Database_Connection</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">await</span> <span class="hljs-keyword">using</span> <span class="hljs-keyword">var</span> conn = <span class="hljs-keyword">new</span> SqlConnection(_connectionString);
        <span class="hljs-keyword">await</span> conn.OpenAsync();

        Assert.True(conn.State == System.Data.ConnectionState.Open);
    }
}
</code></pre>
<p>We now have a much cleaner test class, and all our container logic is handled by the <code>IClassFixture</code> instead. Should you need to add test class specific code, for example seeding the database prior to running, or the mocking of any resources, you can place this code within the constructor.</p>
<h2 id="heading-explanation-of-differences">Explanation of Differences</h2>
<p>We set our <code>Container</code> property as public, rather than private so that our test class can access the container. The test fixture is injected by xUnit's own internal dependency injection mechanics when you use <code>IClassFixture&lt;T&gt;</code>.</p>
<p>xUnit automatically creates an instance of the fixture class and passes it into the test class constructor.</p>
<p>The container is started within the <code>InitializeAsync()</code> method on the <strong>TestFixture</strong> now, rather than the test class, meaning it only gets started once and is readily available for all the tests. This improves performance and test speeds (no more waiting for each container to spin up before each test).</p>
<p>The test flow would look something more like this now:</p>
<p>🟢 InitializeAsync() → Container Created → Container Started</p>
<p>🧪 Running Test_1</p>
<p>🧪 Running Test_2</p>
<p><strong>🛑</strong> DisposeAsync() -&gt; Container Stopped → Container Disposed of</p>
<h3 id="heading-advantages-and-disadvantages">Advantages and Disadvantages</h3>
<h4 id="heading-faster-execution">✅ <strong>Faster Execution</strong></h4>
<p>Significantly reduces setup/teardown overhead, especially when using slow-starting services like databases.</p>
<h4 id="heading-lower-resource-usage">✅ <strong>Lower Resource Usage</strong></h4>
<p>Running a container once per test class consumes far fewer system resources compared to one container per test. This is especially beneficial when running integration tests in CI/CD pipelines where resource usage needs to be optimised to keep costs low.</p>
<h4 id="heading-more-realistic-testing">✅ <strong>More Realistic Testing</strong></h4>
<p>In real-world scenarios, applications don’t restart their databases between API calls, so why should your integration tests?</p>
<h4 id="heading-data-contamination">❌ <strong>Data Contamination</strong></h4>
<p>Effective test data management is essential for maintaining reliable tests. If test data is not properly isolated, it can lead to unintended interference between tests.</p>
<p>For example, a test that creates a new record might introduce unexpected data, causing a retrieval test to fail if it runs afterward. This type of data contamination is a common issue when all tests in a test class share the same database setup. But,with careful test design—such as proper data isolation, cleanup strategies, or using transactional rollbacks—these issues can be mitigated or entirely avoided.</p>
<h4 id="heading-more-care-needs-to-be-taken-around-indempotency">❌ <strong>More Care Needs To Be Taken Around Indempotency</strong></h4>
<p>“Indempotency” refers to the ability to run any test on its own in any order. If the test class is accessing data from the same areas, the assertions may fail when ran in certain orders than others. For example:</p>
<ul>
<li><p>Test_1 inserts a record.</p>
</li>
<li><p>Test_2 assumes the table is empty and asserts <code>QueryByName()</code> should return 1 record</p>
</li>
<li><p>Test_2 fails because Test_1 has already inserted its own record</p>
</li>
</ul>
<h2 id="heading-how-to-share-your-container-across-multiple-test-classes">How to Share Your Container Across Multiple Test Classes</h2>
<p>So we’ve covered a container per test and a container per test class. But what about sharing a container for multiple test classes? Well, it’s as simple as using the <code>ICollectionFixture</code> interface instead of <code>IClassFixture</code>, and it can be used like so:</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">CollectionDefinition(<span class="hljs-meta-string">"Database collection"</span>)</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">DatabaseCollection</span> : <span class="hljs-title">ICollectionFixture</span>&lt;<span class="hljs-title">TestClassFixture</span>&gt;
{
    <span class="hljs-comment">// This class has no code, </span>
    <span class="hljs-comment">// it’s just used to apply the [Collection] attribute to test classes.</span>
}
</code></pre>
<p>The <code>ICollectionFixture&lt;T&gt;</code> mechanism in xUnit automatically ties the fixture instance to all test classes marked with the <code>[Collection("Collection Name")]</code> attribute, for example:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> IntegrationTests;
<span class="hljs-keyword">using</span> Microsoft.Data.SqlClient;

[<span class="hljs-meta">Collection(<span class="hljs-meta-string">"Database collection"</span>)</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">IntegrationFixtureTests</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">string</span> _connectionString;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">IntegrationFixtureTests</span>(<span class="hljs-params">TestClassFixture testClassFixture</span>)</span>
    {
        _connectionString = testClassFixture.Container.GetConnectionString();
    }

    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">Test_Database_Connection</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">await</span> <span class="hljs-keyword">using</span> <span class="hljs-keyword">var</span> conn = <span class="hljs-keyword">new</span> SqlConnection(_connectionString);
        <span class="hljs-keyword">await</span> conn.OpenAsync();

        Assert.True(conn.State == System.Data.ConnectionState.Open);
    }
}

[<span class="hljs-meta">Collection(<span class="hljs-meta-string">"Database collection"</span>)</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">AnotherIntegrationTest</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">string</span> _connectionString;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AnotherIntegrationTest</span>(<span class="hljs-params">TestClassFixture testClassFixture</span>)</span>
    {
        _connectionString = testClassFixture.Container.GetConnectionString();
    }

    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">Another_Database_Test</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">await</span> <span class="hljs-keyword">using</span> <span class="hljs-keyword">var</span> conn = <span class="hljs-keyword">new</span> SqlConnection(_connectionString);
        <span class="hljs-keyword">await</span> conn.OpenAsync();

        Assert.True(conn.State == System.Data.ConnectionState.Open);
    }
}
</code></pre>
<p>Now you can group your integration tests, whether it be all read tests or all write tests – making your tests much more maintainable.</p>
<h2 id="heading-summary-of-approaches">Summary of Approaches:</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Approach</strong></td><td><strong>Container Creation</strong></td><td><strong>Best For</strong></td></tr>
</thead>
<tbody>
<tr>
<td><code>IAsyncLifetime</code> inside the test class</td><td><strong>One per test</strong></td><td>When a fresh DB state per test is needed, avoiding test contamination</td></tr>
<tr>
<td><code>IClassFixture&lt;T&gt;</code> with <code>IAsyncLifetime</code></td><td><strong>One per test class</strong></td><td>Faster execution, sharing DB instance across tests in a class</td></tr>
<tr>
<td><code>ICollectionFixture&lt;T&gt;</code> with <code>IAsyncLifetime</code></td><td><strong>One per multiple test classes</strong></td><td>Sharing a DB instance across different test classes</td></tr>
</tbody>
</table>
</div><h2 id="heading-how-to-create-multiple-containers">How to Create Multiple Containers</h2>
<p>Yes, you can create multiple containers which can host different images, making it perfect for when you have multiple systems you need to integrate with – for example Microsoft SQL Server and a Redis instance.</p>
<p>You can do this by calling the constructor of the relevant TestContainer package like below:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">TestContainersFixture</span> : <span class="hljs-title">IAsyncLifetime</span>
{
    <span class="hljs-keyword">public</span> MsSqlContainer SqlContainer { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">private</span> <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> RedisContainer RedisContainer { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">private</span> <span class="hljs-keyword">set</span>; }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">InitializeAsync</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// SQL Server Container</span>
        SqlContainer = <span class="hljs-keyword">new</span> MsSqlBuilder()
            .WithImage(<span class="hljs-string">"mcr.microsoft.com/mssql/server:2022-latest"</span>)
            .WithPassword(<span class="hljs-string">"P@ssw0rd123"</span>)
            .WithPortBinding(<span class="hljs-number">1433</span>)
            .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(<span class="hljs-number">1433</span>))
            .Build();

        <span class="hljs-comment">// Redis Container</span>
        RedisContainer = <span class="hljs-keyword">new</span> RedisContainerBuilder()
            .WithImage(<span class="hljs-string">"redis:latest"</span>)
            .WithPortBinding(<span class="hljs-number">6379</span>)
            .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(<span class="hljs-number">6379</span>))
            .Build();

        <span class="hljs-keyword">await</span> Task.WhenAll(SqlContainer.StartAsync(), RedisContainer.StartAsync());
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">DisposeAsync</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">await</span> Task.WhenAll(SqlContainer.DisposeAsync(), RedisContainer.DisposeAsync());
    }
}
</code></pre>
<p>And just like that, we have a SQL Server and a Redis instance ready to integrate test against.</p>
<h2 id="heading-how-to-make-your-setup-easier-with-custom-images">How to Make Your Setup Easier With Custom Images</h2>
<p>To make testing easier, and leverage the power of Docker and TestContainers, here’s a great tip. TestContainers fully supports using custom images, including pre-configured ones with seeded databases. Instead of defining everything in the test setup, you can build and use a custom Docker image that already contains the required schema and test data.</p>
<p>When creating your own custom package to use, you can:</p>
<ol>
<li>Upload your custom image to DockerHub and reference from there:</li>
</ol>
<pre><code class="lang-csharp"> SqlContainer = <span class="hljs-keyword">new</span> MsSqlBuilder()
            .WithImage(<span class="hljs-string">"your-dockerhub-username/custom-sql-image"</span>) 
            .WithPassword(<span class="hljs-string">"P@ssw0rd123"</span>)
            .WithPortBinding(<span class="hljs-number">1433</span>)
            .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(<span class="hljs-number">1433</span>))
            .Build();
</code></pre>
<ol start="2">
<li>Build your Docker image locally - f you're using a local image in TestContainers, you can simply reference the image name (e.g., <code>my-custom-sql-image</code>) in your code. TestContainers will first check your local Docker Desktop for the image before attempting to pull it from a registry like Docker Hub.</li>
</ol>
<pre><code class="lang-csharp">SqlContainer = <span class="hljs-keyword">new</span> MsSqlBuilder()
    .WithImage(<span class="hljs-string">"custom-sql-image"</span>) <span class="hljs-comment">// Reference your local image</span>
    .WithPassword(<span class="hljs-string">"P@ssw0rd123"</span>)
    .WithPortBinding(<span class="hljs-number">1433</span>)
    .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(<span class="hljs-number">1433</span>))
    .Build();
</code></pre>
<p>Having a pre-built image can speed up your tests especially in CI/CD pipelines, not to mention make them more readable by removing the seeding code.</p>
<p>To access your custom image in a CI/CD pipeline, you can upload it to DockerHub or GitHub Container Registry (GHCR) and access it from your tests. Build your DockerFile and push it to either system before accessing it in your tests.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Using TestContainers in .NET is a game-changer for integration testing. It’s a lightweight and automated way to manage external dependencies like databases, caching systems, and more. By using test containers in a test class, TestFixture, or ICollectionFixture, you can create cleaner, more reliable tests with isolated environments.</p>
<p>TestContainers can also save you money by eliminating the need for dedicated testing environments with long-lived dependencies. You can create and destroy them on the fly, or even integrate them into your CI/CD pipelines, especially in GitHub where Docker can be easily used.</p>
<p>As always I hope you’ve found this article helpful, and if you have any questions don’t hesitate to reach out on X / Twitter - <a target="_blank" href="https://x.com/grantdotdev">@grantdotdev</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use FakeLogger to Make Testing Easier In .Net ]]>
                </title>
                <description>
                    <![CDATA[ When writing unit tests in .NET, you may need to verify that methods are logging exceptions, errors, or other key information. You might think, No problem, I'll just mock ILogger using my favourite mocking library – for example Moq, NSubstitute, or F... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-fakelogger-to-make-testing-easier-in-net/</link>
                <guid isPermaLink="false">67b3b736d8f6ac012e312ac5</guid>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Mon, 17 Feb 2025 22:24:54 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1739486043718/2d1e6339-fb93-4719-a89a-5b29e30c2bfc.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When writing unit tests in .NET, you may need to verify that methods are logging exceptions, errors, or other key information. You might think, <em>No problem, I'll just mock</em> <code>ILogger</code> <em>using my favourite mocking library</em> – for example Moq, NSubstitute, or FakeItEasy.</p>
<p>While <code>ILogger</code> itself is an interface and can be mocked, many of its commonly used logging methods (like <code>LogInformation()</code>, <code>LogError()</code>, and so on) are what’s called static or extension methods. Since static and extension methods can't be mocked directly, you often need a custom abstraction layer (LoggingService) or a decorator to pass to various other methods or services.</p>
<p>There is another much easier way though. In this article, I will show you how to use the relatively new feature available from .Net 8 upwards called <code>FakeLogger</code>.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-tutorial-setup">Tutorial Setup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-test-the-logging-functionality">How to Test the Logging Functionality</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-fakelogger">How to Use FakeLogger</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-installing-fakelogger-and-fluentassertions">Installing FakeLogger and FluentAssertions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-using-the-fakelogger-class">Using the FakeLogger Class</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-collector">What Is Collector?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-useful-collector-properties">Useful Collector Properties</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-assert-that-structured-log-arguments-are-passed-correctly">How to Assert That Structured Log Arguments Are Passed Correctly</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-verify-that-a-message-has-been-called-at-any-time">How to Verify that a Message Has Been Called at Any Time</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ol>
<h2 id="heading-tutorial-setup">Tutorial Setup</h2>
<p>Let’s imagine you’ve created an online shopping ordering and invoicing service. The logical code tests have been completed, but you now need to test the logging functionality.</p>
<p>For this tutorial we’ll be using the <code>OrderService</code> and <code>InvoiceService</code> classes defined below. I’ve provided comments to illustrate where normally your logic would go, but as this isn’t relevant for the purpose of this tutorial, comments will suffice.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">namespace</span> <span class="hljs-title">FakeLogger_Tutorial</span>;

<span class="hljs-function"><span class="hljs-keyword">public</span> class <span class="hljs-title">OrderService</span>(<span class="hljs-params">ILogger logger, IInvoiceService invoiceService</span>)</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ProcessOrder</span>(<span class="hljs-params">Order order</span>)</span>
    {
        logger.LogInformation(<span class="hljs-string">"Processing order..."</span>);

        <span class="hljs-comment">// Order processing code goes here</span>

        logger.LogInformation(<span class="hljs-string">"Order processed successfully."</span>);

        invoiceService.SendInvoice(order);
    }
}

<span class="hljs-function"><span class="hljs-keyword">public</span> class <span class="hljs-title">InvoiceService</span>(<span class="hljs-params">ILogger logger</span>) : IInvoiceService</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">SendInvoice</span>(<span class="hljs-params">Order order</span>)</span>
    {
        <span class="hljs-comment">// Dispatch order to shipping service</span>
        logger.LogInformation(<span class="hljs-string">"Order dispatched: {OrderId}"</span>, order.ID);

        <span class="hljs-comment">// Generate invoice code</span>

        SendEmail();
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">SendEmail</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Send email to customer</span>
        logger.LogInformation(<span class="hljs-string">"Sending invoice to customer"</span>);

        <span class="hljs-comment">// Perform email sending logic...</span>

        logger.LogInformation(<span class="hljs-string">"Email sent successfully."</span>);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IInvoiceService</span>
{
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">SendInvoice</span>(<span class="hljs-params">Order order</span>)</span>;
}
</code></pre>
<p>As well as a very basic <code>Order</code> and <code>Product</code> classes:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Order</span>
{
    <span class="hljs-keyword">public</span> Guid ID { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> required Guid CustomerId { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

    <span class="hljs-keyword">public</span> List&lt;Product&gt; Products = [];

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">decimal</span> TotalPrice =&gt; Products.Sum(x =&gt; x.Price);

    <span class="hljs-keyword">public</span> DateTime OrderDate { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Product</span>
{
    <span class="hljs-keyword">public</span> Guid ID { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Name { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">decimal</span> Price { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<h2 id="heading-how-to-test-the-logging-functionality">How to Test the Logging Functionality</h2>
<p>Like most aspects of coding, there are multiple ways to achieve this. The recommended approach is to mock the logger and assert against the mocked logger object rather than a concrete instance. This allows for controlled, isolated, and verifiable tests without relying on external dependencies or real logging behaviour – meaning cleaner and more maintainable tests.</p>
<p>You can do this using your preferred mocking library, such as <strong>Moq</strong>, <strong>FakeItEasy</strong>, or <strong>NSubstitute</strong>. You can learn more about these libraries and how to mock successfully in another tutorial I wrote, which you can find <a target="_blank" href="https://www.freecodecamp.org/news/explore-mocking-in-net/">here</a>.</p>
<p>Your initial thoughts may be to write tests like the below example using <code>Moq</code> and <code>XUnit</code> but this won’t work, and I’ll explain why.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> FakeLogger_Tutorial;
<span class="hljs-keyword">using</span> Microsoft.Extensions.Logging;
<span class="hljs-keyword">using</span> Moq;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">UnitTests</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">FailingTestCases</span>
{
    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">LogError_Should_Call_LogError</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> mockLogger = <span class="hljs-keyword">new</span> Mock&lt;ILogger&gt;();

        <span class="hljs-comment">// pass the mockedLogger to our service</span>
        <span class="hljs-keyword">var</span> orderService = <span class="hljs-keyword">new</span> OrderService(
            mockLogger.Object, 
            <span class="hljs-keyword">new</span> Mock&lt;IInvoiceService&gt;().Object
        );

        <span class="hljs-keyword">var</span> customerId = Guid.NewGuid();
        <span class="hljs-keyword">var</span> order = <span class="hljs-keyword">new</span> Order
        {
            ID = Guid.NewGuid(),
            CustomerId = customerId,
            Products = [<span class="hljs-keyword">new</span> Product { ID = Guid.NewGuid(), Name = <span class="hljs-string">"Ping pong balls"</span>, Price = <span class="hljs-number">1.00</span>M }],
            OrderDate = <span class="hljs-keyword">default</span>,
        };

        <span class="hljs-comment">// Act</span>
        orderService.ProcessOrder(order);      

        <span class="hljs-comment">// Assert</span>
        mockLogger.Verify(x =&gt; x.LogInformation(<span class="hljs-string">"Processing order..."</span>), Times.Once);
        mockLogger.Verify(x =&gt; x.LogInformation(<span class="hljs-string">"Order processed successfully."</span>), Times.Once);
    }
}
</code></pre>
<p>When you run this code, it <strong>will</strong> fail with the following error:</p>
<pre><code class="lang-markdown">System.NotSupportedException: 
Unsupported expression: x =&gt; x.LogInformation("Processing order...", new[] {  })
</code></pre>
<h3 id="heading-why-does-this-happen">Why Does This Happen?</h3>
<p>Mocking libraries struggle with static methods like <code>LogInformation</code> because they belong to the type itself, not an instance. Some tools, like JustMock, can handle this using advanced techniques like IL rewriting or shims, but these add complexity.</p>
<p>A common workaround is wrapping <code>ILogger</code> in a logging service for easier testing, along with benefits like abstraction and maintainability. But for a simpler approach, we’ll focus on the new <code>FakeLogger</code> class.</p>
<p>You could test ILogger using the <code>Verify</code> method in Moq, using some overly complicated, verbose methods like below. The test code will work, but it's a bit too complex and hard to read, especially at a glance.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> FakeLogger_Tutorial;
<span class="hljs-keyword">using</span> Microsoft.Extensions.Logging;
<span class="hljs-keyword">using</span> Moq;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">UnitTests</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">FailingTestCases</span>
{
    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">LogError_Should_Call_Logger_LogError</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> mockLogger = <span class="hljs-keyword">new</span> Mock&lt;ILogger&gt;();
        <span class="hljs-keyword">var</span> mockInvoiceService = <span class="hljs-keyword">new</span> Mock&lt;IInvoiceService&gt;();

        <span class="hljs-keyword">var</span> orderService = <span class="hljs-keyword">new</span> OrderService(
            mockLogger.Object, 
            mockInvoice.Object           
        );

        <span class="hljs-keyword">var</span> customerId = Guid.NewGuid();
        <span class="hljs-keyword">var</span> order = <span class="hljs-keyword">new</span> Order
        {
            ID = Guid.NewGuid(),
            CustomerId = customerId,
            Products = [<span class="hljs-keyword">new</span> Product { ID = Guid.NewGuid(), Name = <span class="hljs-string">"Ping pong balls"</span>, Price = <span class="hljs-number">1.00</span>M }],
            OrderDate = <span class="hljs-keyword">default</span>,
        };

        <span class="hljs-comment">// Act</span>
        orderService.ProcessOrder(order);

        <span class="hljs-comment">// Assert</span>
        mockLogger.Verify(logger =&gt; logger.Log(
                It.Is&lt;LogLevel&gt;(logLevel =&gt; logLevel == LogLevel.Information),
                It.Is&lt;EventId&gt;(eventId =&gt; eventId.Id == <span class="hljs-number">0</span>),
                It.Is&lt;It.IsAnyType&gt;((@object, @type) =&gt;
                    @object.ToString() == <span class="hljs-string">"Processing order..."</span>),
                It.IsAny&lt;Exception&gt;(),
                It.IsAny&lt;Func&lt;It.IsAnyType, Exception, <span class="hljs-keyword">string</span>&gt;&gt;()),
            Times.Once);
    }
}
</code></pre>
<h2 id="heading-how-to-use-fakelogger">How to Use FakeLogger</h2>
<p>With .NET 8, we can use the <code>FakeLogger</code> class to make tests clearer for other developers. If you haven’t upgraded yet, I highly recommend it—.NET 8 offers Long-Term Support (LTS) and unlocks many other useful features.</p>
<p>Microsoft defines the class as:</p>
<blockquote>
<p>This type is intended for use in unit tests. It captures all the log state to memory and lets you inspect it to validate that your code is logging what it should.</p>
</blockquote>
<p>In simple terms means that the FakeLogger acts as an in-memory collection of all the Logs and their associated data, meaning we can access these during out Unit Tests. It exposes all the extension methods we would find on the <code>ILogger</code> implementation, making it the perfect way to test our logging functionality.</p>
<h3 id="heading-installing-fakelogger-and-fluentassertions">Installing FakeLogger and FluentAssertions</h3>
<p>FluentAssertions is a great testing library which makes your code easier to test and easier to read. It focuses on using clearly named assertion functions, like <code>Should(), Have()</code> / <code>Be()</code>.</p>
<p>You can install using the Nuget Package Manager within your preferred IDE, or via the terminal with the following command:</p>
<pre><code class="lang-csharp">dotnet <span class="hljs-keyword">add</span> package FluentAssertions
</code></pre>
<p><strong>IMPORTANT: Do not exceed version 7.x.x of FluentAssertions, as v8 comes with a cost, whereas anything prior is free to use.</strong></p>
<p>Once installed, you will need to install <code>Microsoft.Extensions.Diagnostics.Testing</code> as before, using either the Package Console Manager, Terminal, or your preferred method.</p>
<pre><code class="lang-csharp">dotnet <span class="hljs-keyword">add</span> package Microsoft.Extensions.Diagnostics.Testing
</code></pre>
<h3 id="heading-using-the-fakelogger-class">Using the FakeLogger Class</h3>
<p>It is as simple as using any other class in C#. We can instantiate it like so:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> Microsoft.Extensions.Diagnostics.Testing;

<span class="hljs-keyword">var</span> fakeLogger = <span class="hljs-keyword">new</span> FakeLogger();
</code></pre>
<p>Now, rather than passing the <code>mockLogger.Object</code> to our OrderService as before, we shall instead pass our new <code>fakeLoger</code> object like so:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> loggingService = <span class="hljs-keyword">new</span> OrderService(fakeLogger);
</code></pre>
<p>Below is an example of how we can use <code>FakeLogger</code> to check if an <em>Information</em> message was logged.</p>
<pre><code class="lang-csharp">    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">OrderService_ProcessOrder_ShouldLogProgress</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> fakeLogger = <span class="hljs-keyword">new</span> FakeLogger();
        <span class="hljs-keyword">var</span> mockInvoiceService = <span class="hljs-keyword">new</span> Mock&lt;IInvoiceService&gt;();

        <span class="hljs-keyword">var</span> orderService = <span class="hljs-keyword">new</span> OrderService(
            fakeLogger,
            mockInvoiceService.Object
        );

        <span class="hljs-keyword">var</span> customerId = Guid.NewGuid();
        <span class="hljs-keyword">var</span> order = <span class="hljs-keyword">new</span> Order
        {
            ID = Guid.NewGuid(),
            CustomerId = customerId,
            Products = [<span class="hljs-keyword">new</span> Product { ID = Guid.NewGuid(), Name = <span class="hljs-string">"Ping pong balls"</span>, Price = <span class="hljs-number">1.00</span>M }],
            OrderDate = <span class="hljs-keyword">default</span>,
        };

        <span class="hljs-comment">// Act</span>
        orderService.ProcessOrder(order);

        <span class="hljs-comment">// Assert</span>
        fakeLogger.Collector.Count.Should().Be(<span class="hljs-number">2</span>);
        fakeLogger.Collector.LatestRecord.Level.Should().Be(LogLevel.Information);
        fakeLogger.Collector.LatestRecord.Message.Should().Be(<span class="hljs-string">"Order processed successfully."</span>);
    }
</code></pre>
<p>As you can see, it is much easier to read than the previous <code>Moq</code> implementation. The <code>FakeLogger</code> solution combined with <code>FluentAssertions</code> is much more concise and humanly readable to developers of all skillsets.</p>
<h3 id="heading-what-is-collector">What Is <code>Collector</code>?</h3>
<p>The <code>Collector</code> property in <code>FakeLogger</code> is an instance of <code>FakeLogCollector</code>, which collects and stores log information. It stores the messages in the same order they were called, making it easy to assert later.</p>
<h4 id="heading-purpose-of-the-collector-property"><strong>Purpose of the</strong> <code>Collector</code> Property</h4>
<ul>
<li><p>It <strong>stores all log messages</strong> captured by the <code>FakeLogger</code>.</p>
</li>
<li><p>You can access, filter, and assert against logs in your tests.</p>
</li>
<li><p>Useful when verifying structured logs or ensuring correct log levels.</p>
</li>
</ul>
<h3 id="heading-useful-collector-properties">Useful Collector Properties</h3>
<h3 id="heading-latestrecord"><code>LatestRecord</code></h3>
<p>There is more than one way in which you can access and assert logged messages. In the example above, we use the <code>LatestRecord</code> property. The <code>LatestRecord</code> property returns the last <code>FakeLogRecord</code> recorded. This comes from the internal property <code>Records</code>, returning the last record in the List.</p>
<p>The <code>FakeLogRecord</code> object has the following properties:</p>
<pre><code class="lang-csharp">Level
Id 
State
Exception
Message
Scopes
Category
LevelEnabled
Timestamp
</code></pre>
<p>We can therefore check any one of these properties in our assertions.</p>
<h3 id="heading-getsnapshot"><code>GetSnapshot()</code></h3>
<p>GetSnapshot() returns all log records collected.</p>
<ul>
<li><p>This method is useful when you want to inspect <strong>all</strong> logged messages, not just the most recent one.</p>
</li>
<li><p>It returns an <strong>immutable collection</strong>, ensuring that logs are not modified unexpectedly.</p>
</li>
</ul>
<p>As <code>GetSnapshot()</code> returns an immutable collection of messages. We can access these like any other collection of data, whilst also being able to use LINQ to filter, sort, and query the logs. This can be very useful when we would like to assert against the first, last, or any other logged message.</p>
<p>The following test utilises a concrete instance of <code>InvoiceService</code> as we wish to test the actual flow of logs, through both services.</p>
<pre><code class="lang-csharp">    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ProcessOrder_ShouldLogMultipleMessages</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> fakeLog = <span class="hljs-keyword">new</span> FakeLogger();
        <span class="hljs-keyword">var</span> invoiceService = <span class="hljs-keyword">new</span> InvoiceService(fakeLog);
        <span class="hljs-keyword">var</span> orderService = <span class="hljs-keyword">new</span> OrderService(fakeLog, invoiceService);
        <span class="hljs-keyword">var</span> testOrder = <span class="hljs-keyword">new</span> Order
        {
            ID = Guid.NewGuid(),
            CustomerId = Guid.NewGuid(),
            Products =
            [<span class="hljs-meta">
                new Product { ID = Guid.NewGuid(), Name = <span class="hljs-meta-string">"Product 1"</span>, Price = 99.99m },
                new Product { ID = Guid.NewGuid(), Name = <span class="hljs-meta-string">"Product 2"</span>, Price = 199.99m }
            </span>],
        };

        <span class="hljs-comment">// Act</span>
        orderService.ProcessOrder(testOrder);

        <span class="hljs-comment">// Assert</span>
        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">0</span>].Message.Should().Be(<span class="hljs-string">"Processing order..."</span>);
        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">0</span>].Level.Should().Be(LogLevel.Information);

        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">1</span>].Message.Should().Be(<span class="hljs-string">"Order processed successfully."</span>);
        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">1</span>].Level.Should().Be(LogLevel.Information);

        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">2</span>].Message.Should().Be(<span class="hljs-string">$"Order dispatched: <span class="hljs-subst">{testOrder.ID}</span>"</span>);
        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">2</span>].Level.Should().Be(LogLevel.Information);

        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">3</span>].Message.Should().Be(<span class="hljs-string">"Sending invoice to customer"</span>);
        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">3</span>].Level.Should().Be(LogLevel.Information);

        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">4</span>].Message.Should().Be(<span class="hljs-string">"Email sent successfully."</span>);
        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">4</span>].Level.Should().Be(LogLevel.Information);
    }
</code></pre>
<p>This test demonstrates how straightforward it is to assert that the logger captures messages in execution order with the correct <code>LogLevel</code> and message. It also highlights the readability of the test.</p>
<h2 id="heading-how-to-assert-that-structured-log-arguments-are-passed-correctly"><strong>How to Assert That Structured Log Arguments Are Passed Correctly</strong></h2>
<p>Structured logging allows us to pass objects and variables as arguments to log messages, providing richer and more searchable logs. In <code>ILogger</code>, we can pass an object like this:</p>
<pre><code class="lang-csharp">_logger.LogInformation(<span class="hljs-string">"Order processed: {OrderId}"</span>, order.ID);
</code></pre>
<p>By default, logging providers (like the built-in .NET <code>ILogger</code> provider) replace placeholders immediately in the final log message.</p>
<p>With the built-in <code>ILogger</code>, the log message is fully formatted at runtime, for example:</p>
<pre><code class="lang-csharp">_logger.LogInformation(<span class="hljs-string">"Order number {OrderId} dispatched"</span>, <span class="hljs-number">123</span>);
</code></pre>
<p><strong>Final log recorded is:</strong></p>
<pre><code class="lang-csharp"><span class="hljs-string">"Order number 123 dispatched"</span>
</code></pre>
<p>This means that when retrieving logs in tests using the default log provider, we can only verify the final formatted string when using <code>FakeLogger</code> as it captures the fully rendered log message.</p>
<p><strong>Important:</strong> This differs from structured logging providers such as Serilog, where message templates and structured properties are stored separately. In Serilog, the <code>Message</code> column stores the original raw template string, while structured properties / objects are stored in a separate JSON field.</p>
<p>This doesn’t mean you can’t use <code>FakeLogger</code> with Serilog—you absolutely can. But when asserting logs, you must adjust your assertions depending on whether you're verifying the fully formatted message or structured properties.</p>
<p>If we log an order dispatch:</p>
<pre><code class="lang-csharp">logger.LogInformation(<span class="hljs-string">"Order dispatched: {OrderId}"</span>, order.ID);
</code></pre>
<p>Unlike Serilog, <code>FakeLogger</code> does not store <code>{OrderId}</code> as a separate property. Instead, it captures the fully formatted message:</p>
<pre><code class="lang-csharp"><span class="hljs-string">"Order dispatched: 550e8400-e29b-41d4-a716-446655440000"</span>
</code></pre>
<p>Thus, when testing with <code>FakeLogger</code>, we <strong>must</strong> assert against the final formatted string.</p>
<p>Even though <code>FakeLogger</code> does not store the original message template, it does capture structured data separately. This allows you to assert both:</p>
<ol>
<li><p>The final formatted message (since placeholders are replaced at runtime).</p>
</li>
<li><p>The structured data (objects or properties passed as arguments).</p>
</li>
</ol>
<p>The test below asserts the final formatted message, as well as a <code>StructuredState</code> object (the recorded structured log information).</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">InvoiceOrder_ShouldLog_StructuredLogInfo</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> fakeLogger = <span class="hljs-keyword">new</span> FakeLogger&lt;InvoiceService&gt;();
        <span class="hljs-keyword">var</span> service = <span class="hljs-keyword">new</span> InvoiceService(fakeLogger);
        <span class="hljs-keyword">var</span> testOrder = <span class="hljs-keyword">new</span> Order
        {
            ID = Guid.NewGuid(),
            CustomerId = Guid.NewGuid(),
            Products =
            [<span class="hljs-meta">
                new Product { ID = Guid.NewGuid(), Name = <span class="hljs-meta-string">"Product 1"</span>, Price = 99.99m },
                new Product { ID = Guid.NewGuid(), Name = <span class="hljs-meta-string">"Product 2"</span>, Price = 199.99m }
            </span>],
        };

        <span class="hljs-comment">// Act</span>
        service.SendInvoice(testOrder);

        <span class="hljs-comment">// Assert</span>
        fakeLogger.Collector.GetSnapshot()[<span class="hljs-number">0</span>].Message.Should().Be(<span class="hljs-string">$"Order dispatched: <span class="hljs-subst">{testOrder.ID}</span>"</span>);
        <span class="hljs-keyword">var</span> keyValuePairs = fakeLogger.Collector.GetSnapshot()[<span class="hljs-number">0</span>].StructuredState;

        <span class="hljs-keyword">var</span> orderIdProperty = keyValuePairs != <span class="hljs-literal">null</span> &amp;&amp; keyValuePairs
            .Any(x =&gt; x.Key == <span class="hljs-string">"OrderId"</span> &amp;&amp; x.Value == testOrder.ID.ToString());

        orderIdProperty.Should().BeTrue();
    }
</code></pre>
<h2 id="heading-how-to-verify-that-a-message-has-been-called-at-any-time">How to Verify That a Message Has Been Called at Any Time</h2>
<p>What if you want to test that a message or a set of messages are called <strong>anywhere</strong> within the call stack? You can easily do this with the help of LINQ (if you’re not familiar with LINQ you can read it about it in my other article <a target="_blank" href="https://www.freecodecamp.org/news/how-to-use-linq/">here</a>).</p>
<p>We don’t wish to assert that messages are sent in the correct order, just that the messages are logged. We can do this as follows:</p>
<pre><code class="lang-csharp">    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">AllMessages_Should_BeSentInAnyOrder</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> testOrder = <span class="hljs-keyword">new</span> Order
        {
            ID = Guid.NewGuid(),
            CustomerId = Guid.NewGuid(),
            Products =
            [<span class="hljs-meta">
                new Product { ID = Guid.NewGuid(), Name = <span class="hljs-meta-string">"Product 1"</span>, Price = 99.99m },
                new Product { ID = Guid.NewGuid(), Name = <span class="hljs-meta-string">"Product 2"</span>, Price = 199.99m }
            </span>],
        };

        <span class="hljs-keyword">var</span> fakeLogger = <span class="hljs-keyword">new</span> FakeLogger();
        <span class="hljs-keyword">var</span> invoiceService = <span class="hljs-keyword">new</span> InvoiceService(fakeLogger);
        <span class="hljs-keyword">var</span> orderService = <span class="hljs-keyword">new</span> OrderService(fakeLogger, invoiceService);
        <span class="hljs-keyword">var</span> expectedMessages = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt;
        {
            <span class="hljs-string">$"Order Dispatched: <span class="hljs-subst">{testOrder.ID}</span>"</span>,         
            <span class="hljs-string">"Processing order..."</span>,
            <span class="hljs-string">"Invoice sent"</span>
        };

        <span class="hljs-comment">// Act</span>
        orderService.ProcessOrder(testOrder);

        <span class="hljs-comment">// Assert</span>
        fakeLogger.Collector.GetSnapshot()
            .Select(x =&gt; x.Message)
            .Should().IntersectWith(expectedMessages);
    }
</code></pre>
<p>Here, we can utilise the power of LINQ and FluentAssertions to <code>Select</code> each message stored within the <code>Collector</code> property, and then assert that the array of messages can <code>IntersectWith</code> the expected messages.</p>
<p>The <code>IntersectWith</code> method asserts that the collection shares one or more items with the provided collection, a perfect fit for this kind of scenario where we don’t care about the order of logged messages – only that at some point they are logged.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Testing logging in .NET applications has traditionally been tricky because of extension methods in <code>ILogger</code>. But with .NET 8’s <code>FakeLogger</code>, we now have a cleaner, more readable, and efficient way to verify log messages in unit tests.</p>
<p>By using <code>FakeLogger</code> alongside <code>FluentAssertions</code>, we can simplify assertions, improve test readability, and ensure our logging behaviour is correctly implemented without the complexity of traditional mocking libraries.</p>
<p>Whether you're verifying message content, structured logs, or execution order, <code>FakeLogger</code> provides a robust solution that integrates seamlessly into modern .NET testing practices. If you haven't already, I highly recommend upgrading to .NET 8 to take full advantage of this powerful feature.</p>
<p>Hope you found this helpful! If you want to chat more, feel free to reach out on <a target="_blank" href="https://x.com/grantdotdev">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Collections in C# – Lists, Arrays, Dictionaries, and More ]]>
                </title>
                <description>
                    <![CDATA[ One of the first challenges beginners face when developing applications in C# is organising and managing data efficiently. Imagine keeping track of a list of items, mapping unique keys to values, or ensuring there are no duplicates in a collection – ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-collections-in-csharp/</link>
                <guid isPermaLink="false">6793a90ea69c4db6e76c194a</guid>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                    <category>
                        <![CDATA[ beginner ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Fri, 24 Jan 2025 14:51:58 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1737729136643/4cc12d37-da1c-45f0-928f-fbe02d7fdf52.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>One of the first challenges beginners face when developing applications in C# is organising and managing data efficiently. Imagine keeping track of a list of items, mapping unique keys to values, or ensuring there are no duplicates in a collection – these are all common tasks where choosing the right data structure can make a big difference.</p>
<p>C# provides a rich set of built-in data structures, such as <strong>lists</strong>, <strong>dictionaries</strong>, and more, making it easier to work with data differently. Each structure has strengths and is optimised for specific scenarios, so understanding their differences is key to writing clean, efficient, and maintainable code.</p>
<p>In this tutorial, we’ll explore:</p>
<ul>
<li><p><strong>Lists</strong>: Your go-to for dynamic, ordered collections where elements can grow and shrink effortlessly.</p>
</li>
<li><p><strong>Arrays</strong>: The efficient choice for fixed-size collections with predictable memory usage and blazing-fast indexing.</p>
</li>
<li><p><strong>Dictionaries</strong>: Perfect for quick lookups and managing key-value pairs with unmatched speed and clarity.</p>
</li>
<li><p><strong>Stacks</strong>: Ideal for last-in-first-out (LIFO) operations, like tracking history or nested structures.</p>
</li>
<li><p><strong>Queues</strong>: Best for first-in-first-out (FIFO) tasks, like processing jobs or managing sequential workflows.</p>
</li>
<li><p><strong>HashSets:</strong> The choice for collections where uniqueness matters and fast lookups are key.</p>
</li>
</ul>
<p>By the end of this guide, you'll understand the differences between these structures and be equipped to choose the right one for your next project.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-arrays">Arrays in C#</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-lists">Lists in C#</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-dictionaries">Dictionaries in C#</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-hashsets">HashSets in C#</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-queues">Queues in C#</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-stacks">Stacks in C#</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-common-problems">Common Problems</a></p>
</li>
</ol>
<p>For some of the following examples, you’ll need the <code>Animal</code> record below:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Animal</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> Age, <span class="hljs-keyword">string</span> Name, <span class="hljs-keyword">int</span> Legs, <span class="hljs-keyword">string</span> Sound</span>)</span>;
</code></pre>
<h2 id="heading-arrays">Arrays</h2>
<p>An <strong>array</strong> in C# is a fixed-size collection of elements. Arrays are indexed, and their size is set when they are created unlike Lists and other collections. Once defined, the size of an array cannot be changed, making the memory efficient with a low overhead.</p>
<h3 id="heading-single-dimension-arrays">Single-Dimension Arrays</h3>
<p>Arrays are zero-index based, meaning their index begins at 0, rather than 1. If you’re not familiar, an index is a pointer to help you find an item.</p>
<p>For example, if you have 5 names in an Array, the first name is index [0], and the last name would be at index [4].</p>
<p>Arrays are great in scenarios where low-level performance is critical, as they have very little overhead due to their lack of metadata (additional attached information).</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">int</span>[] numbers = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[] { <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span> };

<span class="hljs-keyword">foreach</span>(<span class="hljs-keyword">var</span> number <span class="hljs-keyword">in</span> numbers){
    Console.Write(number);
}
<span class="hljs-comment">//Ouput</span>
<span class="hljs-comment">// 1 2 3 4 5</span>
</code></pre>
<p>In the above example, we instantiate an array with its values (thus giving it a fixed length). But we can assign values after the array is created by using index assignment.</p>
<p><em>Note: You must still specify the size of the array at the time of creation, as the code needs to know the fixed size of the Array.</em></p>
<pre><code class="lang-csharp"><span class="hljs-comment">// create an empty array of 20 indexes</span>
<span class="hljs-keyword">var</span> numbers = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">20</span>];

<span class="hljs-comment">// loop over available indexes and assign `i` </span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; numbers.Length; i++)
{
    numbers[i] = i + <span class="hljs-number">1</span>;
}

<span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">var</span> number <span class="hljs-keyword">in</span> numbers)
{
    Console.Write(<span class="hljs-string">$" <span class="hljs-subst">{number}</span>"</span>);
}
<span class="hljs-comment">// Output</span>
<span class="hljs-comment">//  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20</span>
</code></pre>
<h3 id="heading-multi-dimensional-arrays">Multi-Dimensional Arrays</h3>
<p>Arrays can also be multi-dimensional (for example, rows and columns), meaning they can hold two values. This makes them perfect for building grid-like structures.  </p>
<p>Unlike a jagged array, where each element is an array that can have different lengths, a multidimensional array is a matrix-like structure where each dimension has a fixed size.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Create a 2D multi-dimensional array to represent a chessboard</span>

<span class="hljs-keyword">string</span>[,] chessBoard = <span class="hljs-keyword">new</span> <span class="hljs-keyword">string</span>[<span class="hljs-number">8</span>, <span class="hljs-number">8</span>];

<span class="hljs-comment">// Creating the starting positions of a Chessboard</span>
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>] = <span class="hljs-string">"Rook"</span>;
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">1</span>] = <span class="hljs-string">"Knight"</span>;
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">2</span>] = <span class="hljs-string">"Bishop"</span>;
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">3</span>] = <span class="hljs-string">"Queen"</span>;
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">4</span>] = <span class="hljs-string">"King"</span>;
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">5</span>] = <span class="hljs-string">"Bishop"</span>;
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">6</span>] = <span class="hljs-string">"Knight"</span>;
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">7</span>] = <span class="hljs-string">"Rook"</span>;

chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">0</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">1</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">3</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">4</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">5</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">6</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">7</span>] = <span class="hljs-string">"Pawn"</span>;

chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">0</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">1</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">2</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">3</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">4</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">5</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">6</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">7</span>] = <span class="hljs-string">"Pawn"</span>;

chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">0</span>] = <span class="hljs-string">"Rook"</span>;
chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">1</span>] = <span class="hljs-string">"Knight"</span>;
chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">2</span>] = <span class="hljs-string">"Bishop"</span>;
chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">3</span>] = <span class="hljs-string">"Queen"</span>;
chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">4</span>] = <span class="hljs-string">"King"</span>;
chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">5</span>] = <span class="hljs-string">"Bishop"</span>;
chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">6</span>] = <span class="hljs-string">"Knight"</span>;
chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">7</span>] = <span class="hljs-string">"Rook"</span>;


<span class="hljs-comment">// Print the chessboard</span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> row = <span class="hljs-number">0</span>; row &lt; <span class="hljs-number">8</span>; row++)
{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> col = <span class="hljs-number">0</span>; col &lt; <span class="hljs-number">8</span>; col++)
    {
        <span class="hljs-keyword">string</span> piece = chessBoard[row, col] ?? <span class="hljs-string">"Empty"</span>;
        Console.Write(<span class="hljs-string">$"<span class="hljs-subst">{piece}</span>\t"</span>);
    }
    Console.WriteLine();
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-bash">Rook    Knight  Bishop  Queen   King    Bishop  Knight  Rook    
Pawn    Pawn    Pawn    Pawn    Pawn    Pawn    Pawn    Pawn    
Empty   Empty   Empty   Empty   Empty   Empty   Empty   Empty   
Empty   Empty   Empty   Empty   Empty   Empty   Empty   Empty   
Empty   Empty   Empty   Empty   Empty   Empty   Empty   Empty   
Empty   Empty   Empty   Empty   Empty   Empty   Empty   Empty   
Pawn    Pawn    Pawn    Pawn    Pawn    Pawn    Pawn    Pawn    
Rook    Knight  Bishop  Queen   King    Bishop  Knight  Rook
</code></pre>
<h3 id="heading-jagged-array">Jagged Array</h3>
<p>Welcome to inception. A <strong>jagged array</strong> in C# is an array of arrays, where each "inner" array can have a different length.</p>
<p>Unlike multi-dimensional arrays, jagged arrays are not rectangular, meaning the rows can have varying sizes.</p>
<p>A usage example could be building a Calendar app. Below is a basic usage outputting the days of each month in the year:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">int</span>[][] daysInMonths = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">12</span>][];

<span class="hljs-comment">// Initialize each month with its corresponding number of days</span>
daysInMonths[<span class="hljs-number">0</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">31</span>]; <span class="hljs-comment">// January</span>
daysInMonths[<span class="hljs-number">1</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">28</span>]; <span class="hljs-comment">// February (non-leap year)</span>
daysInMonths[<span class="hljs-number">2</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">31</span>]; <span class="hljs-comment">// March</span>
daysInMonths[<span class="hljs-number">3</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">30</span>]; <span class="hljs-comment">// April</span>
daysInMonths[<span class="hljs-number">4</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">31</span>]; <span class="hljs-comment">// May</span>
daysInMonths[<span class="hljs-number">5</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">30</span>]; <span class="hljs-comment">// June</span>
daysInMonths[<span class="hljs-number">6</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">31</span>]; <span class="hljs-comment">// July</span>
daysInMonths[<span class="hljs-number">7</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">31</span>]; <span class="hljs-comment">// August</span>
daysInMonths[<span class="hljs-number">8</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">30</span>]; <span class="hljs-comment">// September</span>
daysInMonths[<span class="hljs-number">9</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">31</span>]; <span class="hljs-comment">// October</span>
daysInMonths[<span class="hljs-number">10</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">30</span>]; <span class="hljs-comment">// November</span>
daysInMonths[<span class="hljs-number">11</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">31</span>]; <span class="hljs-comment">// December</span>

<span class="hljs-comment">// Print the number of days in each month</span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> month = <span class="hljs-number">0</span>; month &lt; daysInMonths.Length; month++)
{
    Console.WriteLine(<span class="hljs-string">$"Month <span class="hljs-subst">{month + <span class="hljs-number">1</span>}</span>: <span class="hljs-subst">{daysInMonths[month].Length}</span> days"</span>);
}
</code></pre>
<p>You should use an <code>Array</code> in:</p>
<ul>
<li><p><strong>Performance-critical applications</strong> where memory overhead and speed matter.</p>
</li>
<li><p><strong>Fixed data sets</strong> where the size will not change.</p>
</li>
<li><p><strong>Multi-dimensional data</strong>, for example, graph coordinates (x, y)</p>
</li>
</ul>
<h2 id="heading-lists">Lists</h2>
<p>A <code>List&lt;T&gt;</code> in C# is a resizable collection of items of the same type, signaled above by the letter <code>T</code>. It allows adding, removing, and accessing items by index. Unlike arrays, lists grow dynamically as needed.  </p>
<p>Commonly used for sequential data, they support <a target="_blank" href="https://www.freecodecamp.org/news/how-to-use-linq/">LINQ</a> queries and various utility methods for data manipulation.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> animals = <span class="hljs-keyword">new</span> List&lt;Animal&gt;()
{
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">10</span>, <span class="hljs-string">"Dog"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Woof"</span>),
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">5</span>, <span class="hljs-string">"Cat"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Meow"</span>),
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">2</span>, <span class="hljs-string">"Lion"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Roar"</span>),
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">6</span>, <span class="hljs-string">"Giraffe"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Trumpet"</span>),
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">15</span>, <span class="hljs-string">"Red-Panda"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Squeak"</span>)    
};

animals.Add(<span class="hljs-keyword">new</span> Animal(<span class="hljs-number">2</span>, <span class="hljs-string">"Hamster"</span>,<span class="hljs-number">4</span>,<span class="hljs-string">"Squeak"</span>));
animals.Remove(x=&gt;x.Sound == <span class="hljs-string">"Meow"</span>); <span class="hljs-comment">// Remove all squeaking animals</span>
</code></pre>
<p>Lists are a highly versatile data structure, where the order of items remains the same order in which they are added or removed (no manipulation. For example, whenever you call <code>.Add()</code> a method on a list, it will append the item to the list, and the order stays the same as before but with the additional animal.</p>
<p>You can modify the data (for example, filter, map, or sort) before sending lists to other areas of your application thanks to the extensive utility methods available in the <code>List&lt;T&gt;</code> class.</p>
<h2 id="heading-dictionaries">Dictionaries</h2>
<p>Dictionaries work just like the term we know in the English language.</p>
<p>We have a key (a lookup term) and a value (the mapped object or data). Because of this, you might hear the term 'key-value pair' when referring to dictionaries.</p>
<p>Dictionaries are best used to efficiently retrieve data based on a unique identifier, such as an ID, name, or other uniquely identifying fields. They ensure their unique keys are ideal for scenarios requiring optimal performance without iterative searching.  </p>
<p>I recommend using dictionaries when the order of elements is unimportant and you need to represent relationships, such as mapping countries to capitals, products to prices, or people to addresses.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> animalDictionary = <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, Animal&gt;()
{
    { <span class="hljs-string">"Dog"</span>, <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">10</span>, <span class="hljs-string">"Dog"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Woof"</span>) },
    { <span class="hljs-string">"Cat"</span>, <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">5</span>, <span class="hljs-string">"Cat"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Meow"</span>) },
    { <span class="hljs-string">"Elephant"</span>, <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">8</span>, <span class="hljs-string">"Elephant"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Trumpet"</span>) },
    { <span class="hljs-string">"Lion"</span>, <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">2</span>, <span class="hljs-string">"Lion"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Roar"</span>) },
    { <span class="hljs-string">"Giraffe"</span>, <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">6</span>, <span class="hljs-string">"Giraffe"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Trumpet"</span>) },
};
<span class="hljs-comment">// Add</span>
animalDictionary.Add(<span class="hljs-string">"Red panda"</span>, <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">2</span>, <span class="hljs-string">"Red Panda"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Squeaker"</span>));

<span class="hljs-comment">// Remove</span>
animalDictionary.Remove(<span class="hljs-string">"Cat"</span>);

<span class="hljs-comment">//Get</span>
<span class="hljs-keyword">var</span> giraffe = animalDictionary[<span class="hljs-string">"Giraffe"</span>];
</code></pre>
<h2 id="heading-hashsets">HashSets</h2>
<p>A <code>HashSet&lt;T&gt;</code> is a collection in C# that stores unique elements. It uses a hash-based implementation to ensure very efficient lookups, additions, and deletions. This means it uses hash functions to quickly map keys to values, you can read more about that here.</p>
<p>Duplicate elements are automatically ignored.</p>
<p>How does this differ from a Dictionary? HashSets don't have keys like Dictionaries. Instead, they store values directly and are accessed by iterating over the elements using a <code>foreach</code> loop or LINQ queries.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> animalHashSet = <span class="hljs-keyword">new</span> HashSet&lt;Animal&gt;()
{
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">3</span>, <span class="hljs-string">"Lion"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Roar"</span>),
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">5</span>, <span class="hljs-string">"Tiger"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Roar"</span>),
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">2</span>, <span class="hljs-string">"Elephant"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Trumpet"</span>),
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">1</span>, <span class="hljs-string">"Giraffe"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Neigh"</span>)
};
<span class="hljs-comment">// Add</span>
animalHashSet.Add(<span class="hljs-keyword">new</span> Animal(<span class="hljs-number">3</span>, <span class="hljs-string">"Lion"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Roar"</span>));
<span class="hljs-comment">// Remove</span>
animalHashSet.Remove(x=&gt;x.Sound == <span class="hljs-string">"Neigh"</span>);
<span class="hljs-comment">// Get</span>
animalHashSet.FirstOrDefault(x=&gt;x.Name == <span class="hljs-string">"Elephant"</span>);
</code></pre>
<p>Above, we create a <code>HashSet&lt;Animal&gt;</code> and attempt to add a duplicate object. You may expect this to throw an error, as we know HashSets can only store unique values. But instead it handles it quite beautifully and simply doesn’t add the duplicate object, so the output is:</p>
<pre><code class="lang-bash">Animal { Age = 3, Name = Lion, Legs = 4, Sound = Roar }
Animal { Age = 5, Name = Tiger, Legs = 4, Sound = Roar }
Animal { Age = 2, Name = Elephant, Legs = 4, Sound = Trumpet }
</code></pre>
<h2 id="heading-queues">Queues</h2>
<p>Queues work in just the same way as a queue does in everyday life, with a first-in, first-out approach.</p>
<p><code>Queue&lt;T&gt;</code> does not implement the ICollection interface like Dictionaries and Lists, meaning it doesn't have an <strong>Add()</strong> method. This means you cannot add elements to the Queue whilst instantiating. It also means you cannot use the <strong>Add()</strong> method to add items – instead, you use the <strong>Enqueue()</strong> method.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> arc = <span class="hljs-keyword">new</span> Queue&lt;<span class="hljs-keyword">string</span>&gt;();
arc.Enqueue(<span class="hljs-string">"2 Lions"</span>);
arc.Enqueue(<span class="hljs-string">"2 Tigers"</span>);
arc.Enqueue(<span class="hljs-string">"2 Bears"</span>);

<span class="hljs-comment">// Peek method allows to peek at the front of the queue</span>
Console.WriteLine(<span class="hljs-string">"Front of the Queue: "</span> + arc.Peek());

<span class="hljs-comment">// Output</span>
<span class="hljs-comment">// Front of the Queue: 2 Lions</span>

Console.WriteLine(<span class="hljs-string">$"Processing: <span class="hljs-subst">{arc.Dequeue()}</span>"</span>); <span class="hljs-comment">// Output: 2 Lions</span>
Console.WriteLine(<span class="hljs-string">$"Processing: <span class="hljs-subst">{arc.Dequeue()}</span>"</span>); <span class="hljs-comment">// Output: 2 Tigers</span>
</code></pre>
<p>The <code>Dequeue</code> method not only returns the next item in the queue but also removes the item from the queue as expected. You can also clear the queue using the <code>Clear()</code> method.</p>
<h2 id="heading-stacks">Stacks</h2>
<p>Stacks work oppositely to <code>Queue&lt;T&gt;</code>, in that instead of first-in-first-out, they work on a last-in-first-out mechanic.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> stack = <span class="hljs-keyword">new</span> Stack&lt;<span class="hljs-keyword">int</span>&gt;();
stack.Push(<span class="hljs-number">1</span>);
stack.Push(<span class="hljs-number">2</span>);
stack.Push(<span class="hljs-number">3</span>);
stack.Push(<span class="hljs-number">4</span>);
stack.Push(<span class="hljs-number">5</span>);

<span class="hljs-comment">// iterate over the Stack</span>
<span class="hljs-keyword">foreach</span>(<span class="hljs-keyword">var</span> number <span class="hljs-keyword">in</span> stack )
{
    Console.WriteLine(number);
}

<span class="hljs-comment">// Output</span>
<span class="hljs-comment">// 5 4 3 2 1</span>
</code></pre>
<p>You may think looping through the items in a <code>Stack</code> would work the same as a List or Queue and would still print them out in order of going in. But the system knows it’s a stack, and so it enumerates the items in reverse order of how they were added – that is, the most recently added element (<code>5</code>) is returned first.</p>
<p>You can also utilise the <code>Pop()</code> method which will return the last item in the collection, and remove it at the same time.</p>
<h2 id="heading-common-problems">Common Problems</h2>
<p>When using various collections, you will more than likely come across common problems, such as <code>KeyNotFoundException</code> when using dictionaries, <code>IndexOutOfRangeException</code> on lists/arrays, or <code>InvalidOperationException</code> when modifying a collection during iteration.</p>
<h3 id="heading-keynotfoundexception"><code>KeyNotFoundException</code></h3>
<p><strong>Scenario:</strong> You try to access a Dictionary key that doesn’t exist in the Dictionary. This will result in a <code>KeyNotFoundException</code>, and error.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> dictionary = <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>&gt;()
{
    { <span class="hljs-string">"Morning"</span>, <span class="hljs-string">"Good Morning"</span> },
    { <span class="hljs-string">"Afternoon"</span>, <span class="hljs-string">"Good afternoon"</span> },
    { <span class="hljs-string">"Evening"</span>, <span class="hljs-string">"Good evening"</span> },
    { <span class="hljs-string">"Night"</span>, <span class="hljs-string">"Good night"</span> },
};

<span class="hljs-keyword">var</span> message = dictionary[<span class="hljs-string">"Dusk"</span>];

Console.WriteLine(message);

<span class="hljs-comment">// Output</span>
<span class="hljs-comment">//Unhandled exception. System.Collections.Generic.KeyNotFoundException: The given key 'Dusk' was not present in the dictionary.</span>
</code></pre>
<p><strong>Solution:</strong> I recommend using the <code>TryGetValue</code> function, which will handle it gracefully and return the item as an <code>out</code> parameter (if it can be found, otherwise the default value).</p>
<p><code>TryGetValue</code> returns a boolean to show whether it could or couldn’t find the provided key. This boolean value can then be utilised to determine functionality based on successful retrieval or not, rather than checking the output parameter, for example (if it is null/empty or not).</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> dictionary = <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>&gt;()
{
    { <span class="hljs-string">"Morning"</span>, <span class="hljs-string">"Good Morning"</span> },
    { <span class="hljs-string">"Afternoon"</span>, <span class="hljs-string">"Good afternoon"</span> },
    { <span class="hljs-string">"Evening"</span>, <span class="hljs-string">"Good evening"</span> },
    { <span class="hljs-string">"Night"</span>, <span class="hljs-string">"Good night"</span> },
};

<span class="hljs-keyword">var</span> input = Console.ReadLine(); <span class="hljs-comment">// Dusk</span>

dictionary.TryGetValue(input, <span class="hljs-keyword">out</span> <span class="hljs-keyword">var</span> message);
Console.WriteLine(<span class="hljs-string">$"`Message:<span class="hljs-subst">{message}</span>`"</span>);
<span class="hljs-comment">// Ouput = `Message ` (blank message as default string value is empty string</span>

<span class="hljs-comment">//or check if was able to retrieve and access output if it was</span>
<span class="hljs-keyword">if</span>(dictionary.TryGetValue(input,<span class="hljs-keyword">out</span> <span class="hljs-keyword">var</span> m)){
    Console.WriteLine(message);
}
</code></pre>
<h3 id="heading-indexoutofrangeexception-listsarrays"><code>IndexOutOfRangeException</code> (Lists/Arrays):</h3>
<p><strong>Scenario</strong>: Trying to access an index that’s outside the valid range of a list or array.  </p>
<p>As we know, Arrays are 0 index-based, so trying to access an index of [5] on an Array of 5 items will throw the <code>IndexOutOfRangeException</code>.</p>
<p><strong>Solutions:</strong></p>
<ol>
<li>Ensure the index is within bounds using <code>list.Count</code> or <code>array.Length</code> before access.</li>
</ol>
<ol start="2">
<li>Use the <code>ElementAtOrDefault()</code> method. If it can’t access an item at the given index, it will return the default value, which can then be handled accordingly.</li>
</ol>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> names = <span class="hljs-keyword">new</span> <span class="hljs-keyword">string</span>[]
{
    <span class="hljs-string">"Tony"</span>, <span class="hljs-string">"Clint"</span>, <span class="hljs-string">"Bob"</span>, <span class="hljs-string">"Alice"</span>, <span class="hljs-string">"Lisa"</span>
};

<span class="hljs-keyword">var</span> name = names.ElementAtOrDefault(<span class="hljs-number">6</span>);
Console.WriteLine(name ?? <span class="hljs-string">"Name not found."</span>);
</code></pre>
<h3 id="heading-invalidoperationexception-iterating-collections"><code>InvalidOperationException</code> (Iterating Collections):</h3>
<p><strong>Scenario</strong>: Modifying a collection (for example, adding or removing items) while iterating over it with a <code>foreach</code> loop will throw an <code>InvalidOperationException</code> because you're trying to remove an item from the list while iterating over it with a <code>foreach</code> loop.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> myList = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt; { <span class="hljs-string">"Apple"</span>, <span class="hljs-string">"Banana"</span>, <span class="hljs-string">"Cherry"</span>, <span class="hljs-string">"Banana"</span> };

<span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">var</span> item <span class="hljs-keyword">in</span> myList)
{
    <span class="hljs-keyword">if</span> (item == <span class="hljs-string">"Banana"</span>)
    {
        myList.Remove(item); <span class="hljs-comment">// Throws InvalidOperationException</span>
    }
}
</code></pre>
<h4 id="heading-why-this-happens"><strong>Why This Happens:</strong></h4>
<ul>
<li><p>The <code>foreach</code> loop maintains an internal enumerator for the collection.</p>
</li>
<li><p>Modifying the collection (for example, adding/removing items) invalidates the enumerator, which causes the runtime to throw an <code>InvalidOperationException</code>.</p>
</li>
</ul>
<p><strong>Solution 1: Use a</strong> <code>for</code> <strong>Loop</strong></p>
<p>You can use a <code>for</code> loop with an index to safely modify the list during iteration:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> fruits = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt; { <span class="hljs-string">"Apple"</span>, <span class="hljs-string">"Banana"</span>, <span class="hljs-string">"Cherry"</span>, <span class="hljs-string">"Banana"</span> };

<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; fruits; i++)
{
    <span class="hljs-keyword">if</span> (fruits[i] == <span class="hljs-string">"Banana"</span>)
    {
        fruits.RemoveAt(i);
        <span class="hljs-comment">// Adjust the index to account for the removed item</span>
        i--; 
    }
}
Console.WriteLine(<span class="hljs-keyword">string</span>.Join(<span class="hljs-string">", "</span>, fruits)); 
<span class="hljs-comment">// Output: Apple, Cherry</span>
</code></pre>
<p><strong>Solution 2: Iterate Over a Copy</strong></p>
<p>Another approach is to iterate over a copy of the list using <code>ToList()</code>. This way, you’re not directly iterating over the original collection, so modifications won’t affect the loop.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> originalList = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt; { <span class="hljs-string">"Apple"</span>, <span class="hljs-string">"Banana"</span>, <span class="hljs-string">"Cherry"</span>, <span class="hljs-string">"Banana"</span> };

<span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">var</span> item <span class="hljs-keyword">in</span> originalList.ToList()) <span class="hljs-comment">// Create a copy</span>
{
    <span class="hljs-keyword">if</span> (item == <span class="hljs-string">"Banana"</span>)
    {
        originalList.Remove(item); <span class="hljs-comment">// Safe removal</span>
    }
}
Console.WriteLine(<span class="hljs-keyword">string</span>.Join(<span class="hljs-string">", "</span>, originalList)); 
<span class="hljs-comment">// Output: Apple, Cherry</span>
</code></pre>
<p><strong>Solution 3: Use LINQ to Filter</strong></p>
<p>If you only want to remove items based on a condition, you can use LINQ to create a new filtered list:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> fruits = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt; { <span class="hljs-string">"Apple"</span>, <span class="hljs-string">"Banana"</span>, <span class="hljs-string">"Cherry"</span>, <span class="hljs-string">"Banana"</span> };

fruits = fruits.Where(item =&gt; item != <span class="hljs-string">"Banana"</span>).ToList(); <span class="hljs-comment">// Filter out "Banana"</span>

Console.WriteLine(<span class="hljs-keyword">string</span>.Join(<span class="hljs-string">", "</span>, fruits)); 
<span class="hljs-comment">// Output: Apple, Cherry</span>
</code></pre>
<h2 id="heading-closing-thoughts">Closing Thoughts</h2>
<p>In this article, you’ve learned about many of the common Data Structures for storing multiple objects and values.</p>
<p>Whether you're storing data in a fixed-size array, managing a dynamic list, working with first-in-first-out queues, last-in-first-out stacks, or key-value pair dictionaries, knowing when and how to use each collection is key to becoming a confident and proficient C# developer.</p>
<p>Mastering these concepts will not only improve your ability to handle data effectively but also lay the groundwork for more advanced topics in data structures and algorithms. Combining these data structures with LINQ can provide some performant and easy-to-use mechanics. To learn more about LINQ you can check out my article <a target="_blank" href="https://www.freecodecamp.org/news/how-to-use-linq/">here</a>.  </p>
<p>As you continue your coding journey, keep experimenting with these collections, apply them in real-world scenarios, and deepen your understanding of their inner workings.</p>
<p>As always should you wish to discuss this article further, any other coding-related problems, or hear about other articles I’m writing, drop me a follow on <a target="_blank" href="https://x.com/grantdotdev">X(Twitter)</a>  </p>
<p>Happy coding! 😊</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Benchmark Your Code in C# ]]>
                </title>
                <description>
                    <![CDATA[ Knowing how your code performs is a crucial part of development. We strive to write the most optimal and performant code whilst keeping readability. In this article, I will show you how to test the performance of your code, benchmark your code, and i... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-benchmark-your-code-in-csharp/</link>
                <guid isPermaLink="false">673bdcd6088133abf555d52f</guid>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Tue, 19 Nov 2024 00:33:26 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731752379027/4ec760c3-4183-4852-9d3d-e3a5c75b4bcf.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Knowing how your code performs is a crucial part of development. We strive to write the most optimal and performant code whilst keeping readability.</p>
<p>In this article, I will show you how to test the performance of your code, benchmark your code, and identify areas of improvement within your code base.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-benchmarking">What is Benchmarking?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-using-a-stopwatch-is-not-reliable">Why Using a Stopwatch is Not Reliable</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-benchmarkdotnet">How to Use BenchMarkDotnet</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-else-can-you-test-with-benchmarkdotnet">What Else Can You Test with BenchmarkDotnet?</a></p>
</li>
</ul>
<h2 id="heading-what-is-benchmarking">What is Benchmarking?</h2>
<p>Benchmarking measures the performance of your code, application, system, or hardware under specific conditions.  </p>
<p>The goal is to gather precise data about how the system behaves for metrics like processing speed, memory usage, resource consumption, or throughput and to identify areas where performance can be optimized.</p>
<h2 id="heading-why-using-a-stopwatch-is-not-reliable">Why Using a Stopwatch is Not Reliable</h2>
<p>Using the <code>Stopwatch</code> class for benchmarking in C# comes with many problems. Although it provides a simple way to measure elapsed time on a method or process, it lacks the precision, control, and consistency needed for accurate benchmarking.  </p>
<p>Before I get into the negatives of this utility, let’s look at how you could use it for very simple tasks.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> System.Diagnostics;

<span class="hljs-comment">// Create a new Stopwatch instance</span>
<span class="hljs-keyword">var</span> sw = <span class="hljs-keyword">new</span> Stopwatch();

<span class="hljs-comment">// Start the stop watch clock</span>
sw.Start();

<span class="hljs-comment">// run your code</span>
<span class="hljs-keyword">var</span> sum = <span class="hljs-number">0</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">100</span>; i++)
{
    sum += i * i;
    Console.WriteLine(<span class="hljs-string">$"<span class="hljs-subst">{sw.ElapsedMilliseconds}</span>"</span>);
}
<span class="hljs-comment">// Stop the clock !</span>
sw.Stop();

<span class="hljs-comment">// Output total time elapsed on the Stopwatch.</span>
Console.WriteLine(<span class="hljs-string">$"Elapsed time: <span class="hljs-subst">{sw.ElapsedMilliseconds}</span> ms"</span>);
</code></pre>
<p>This would print out how many milliseconds have elapsed on each iteration as well as the end elapsed milliseconds. As this is a short program, you can convert to nanoseconds by using <code>ticks</code> like so:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">long</span> ticks = stopwatch.ElapsedTicks;
<span class="hljs-keyword">double</span> nanoseconds = (ticks * <span class="hljs-number">1e9</span>) / Stopwatch.Frequency;
</code></pre>
<p>Using a stopwatch can be useful if you want to quickly compare two methods or identify obvious performance bottlenecks during development. It’s a lightweight way to get an initial sense of which sections of code might need optimization.</p>
<h3 id="heading-cons-of-stopwatch">Cons of Stopwatch</h3>
<ul>
<li><p>Lack of precision by default, being only accurate to around 100 nanoseconds, which may not be useful for smaller quick micro operations.</p>
</li>
<li><p>JIT (Just in Time) compilation - When code runs for the first time, a JIT compiler compiles the code before running, causing a delay and skewing the timing of completion. Subsequent runs of the code will be slightly faster, however, <code>Stopwatch</code> does not account for this. Keeping this in mind, it is worth running the code a few times to try and alleviate this problem.</p>
</li>
<li><p>Garbage Collection (GC) - If garbage collection happens during a <code>Stopwatch</code> measurement, the time recorded will include GC pause time, which does not reflect the actual execution time of your code.</p>
</li>
</ul>
<p>These are just some of the basic, and most common flaws of using a <code>Stopwatch</code> to test the performance of your code but there are others.  </p>
<p>So what is the best approach?</p>
<p><strong>BenchmarkDotNet</strong> is a popular and robust library for benchmarking in .NET, which can be installed using <code>nuget</code>.</p>
<p>It overcomes many of the above challenges, in the following ways:</p>
<ul>
<li><p>Code Warm Ups - Automatically warms up the code (by running the code a few times) to avoid JIT-related inaccuracies.</p>
</li>
<li><p>Multiple code iterations - Runs the code multiple times to analyze and calculates statistical summaries, around execution time, heap memory allocation and more. The number of times the code is ran can be configured.</p>
</li>
<li><p>Isolated Environments - Manages garbage collection and isolates the execution environment to reduce external interference.</p>
</li>
</ul>
<h2 id="heading-how-to-use-benchmarkdotnet">How to Use BenchMarkDotnet</h2>
<p>Firstly, we need to install the Nuget package. To do this, run the following command in your command line/terminal:</p>
<pre><code class="lang-plaintext">dotnet add package BenchmarkDotnet
</code></pre>
<p>We then need some methods to benchmark, so create a .Net 8 C# console app with the following two class files:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Program.cs</span>
<span class="hljs-keyword">using</span> BenchmarkDotNet.Running;

BenchmarkRunner.Run&lt;Benchmarks&gt;();
</code></pre>
<pre><code class="lang-csharp"><span class="hljs-comment">//Benchmarks.cs</span>
<span class="hljs-keyword">using</span> BenchmarkDotNet.Attributes;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Benchmarks</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">int</span>[] _numbers = Enumerable.Range(<span class="hljs-number">1</span>, <span class="hljs-number">1000</span>).ToArray();

    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">ForLoopSum</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">int</span> sum = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; _numbers.Length; i++)
        {
            sum += _numbers[i];
        }

        <span class="hljs-keyword">return</span> sum;
    }

    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">ForeachLoopSum</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">var</span> sum = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">int</span> number <span class="hljs-keyword">in</span> _numbers)
        {
            sum += number;
        }

        <span class="hljs-keyword">return</span> sum;
    }

    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">LinqSelect</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">return</span> _numbers.Sum();
    }
}
</code></pre>
<p>Above, we have 3 different methods to add up an array of integers, each doing so in a slightly different fashion. This is a perfect example to show how benchmarking can aid us to choose the best solution in our code base.</p>
<h3 id="heading-how-to-run-the-benchmarks">How to Run the Benchmarks</h3>
<p>To run the benchmarks, you can run the following commands in your terminal/command line.</p>
<pre><code class="lang-bash">dotnet build
<span class="hljs-comment"># then run </span>
dotnet run -c Release
</code></pre>
<p>BenchmarkDotnet will then run the methods marked with the <code>[Benchmark]</code> attribute multiple times, and will output the results in an easy to read table, like so:</p>
<pre><code class="lang-bash">| Method         | Mean     | Error   | StdDev  |
|--------------- |---------:|--------:|--------:|
| ForLoopSum     | 434.2 ns | 0.40 ns | 0.31 ns |
| ForeachLoopSum | 321.9 ns | 1.22 ns | 1.14 ns |
| LinqSelect     | 189.4 ns | 0.84 ns | 0.70 ns |
</code></pre>
<p>What does this mean?</p>
<p><strong>Method</strong> - Name of the method under test  </p>
<p><strong>Mean</strong> - Shows the mean (average) time it took in nanoseconds.  </p>
<p><strong>Error</strong> - Represents the margin of error, telling you how much the "Mean" result might vary due to random factors in the system. The lower the number the better, here you can see a very small margin of error meaning the results are stable, whilst large numbers would mean more uncertainty/ unreliable results.</p>
<p><strong>StdDev -</strong> (Standard Deviation) shows how consistent the benchmark results are. A low deviation score indicates the time taken was very similar across multiple runs, increasing reliability. If the standard deviation is high, it would mean the method’s execution time varied a lot between runs.</p>
<h3 id="heading-how-to-measure-memory-allocation">How to Measure Memory Allocation</h3>
<p>Knowing how fast your methods run is a great statistic to understand and know. However, your performance and optimization isn’t just about the time of execution, sometimes you should ensure that there are no memory leaks or large sums of memory utilized, especially with large execution process.</p>
<p>We can use the <code>[MemoryDiagnoser]</code> to the <code>Benchmarks</code> class, which informs the benchmarking library to include memory statistics to the methods under test.</p>
<p>When we run our benchmarks, we get the following output:</p>
<pre><code class="lang-bash">| Method         | Mean     | Error   | StdDev  | Allocated |
|--------------- |---------:|--------:|--------:|----------:|
| ForLoopSum     | 436.8 ns | 5.32 ns | 4.98 ns |         - |
| ForeachLoopSum | 324.6 ns | 2.20 ns | 2.06 ns |         - |
| LinqSelect     | 192.7 ns | 2.40 ns | 2.24 ns |         - |
</code></pre>
<p>But wait, the <strong>Allocated</strong> column has but a dash ? Where are the results?</p>
<p>Simple operations, like summing values in an array generally don’t allocate memory, as they often only use stack memory, which BenchmarkDotNet doesn’t track in the same way.</p>
<p>But using the following tests, we can see how memory allocation can be analyzed:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">MemoryBenchmark</span>
{
    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-title">StringConcatenation</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">string</span> result = <span class="hljs-string">""</span>;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1000</span>; i++)
        {
            result += <span class="hljs-string">"text"</span>;
        }
        <span class="hljs-keyword">return</span> result;
    }

    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-title">StringBuilderConcatenation</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">var</span> builder = <span class="hljs-keyword">new</span> System.Text.StringBuilder();
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1000</span>; i++)
        {
            builder.Append(<span class="hljs-string">"text"</span>);
        }
        <span class="hljs-keyword">return</span> builder.ToString();
    }
}
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">| Method                     | Mean       | Error     | StdDev    | Gen0     | Allocated  |
|--------------------------- |-----------:|----------:|----------:|---------:|-----------:|
| StringConcatenation        | 218.930 us | 0.7230 us | 0.6409 us | 641.8457 | 3933.56 KB |
| StringBuilderConcatenation |   1.645 us | 0.0034 us | 0.0030 us |   2.6875 |   16.47 KB |
</code></pre>
<p>Here we have 2 new columns:<br><strong>Gen0 Column:</strong><br>The <strong>Gen0</strong> column indicates how many Gen 0 garbage collections occurred during each method’s execution.  </p>
<p>.Net uses a generational garbage collection system, where memory is divided into three "generations" (Gen0, Gen1, and Gen2).</p>
<ul>
<li><p><strong>Gen0 (Generation 0)</strong>: Holds short-lived objects, such as temporary variables and small, quickly discarded objects. Gen0 collections are the fastest type of GC but still introduce some overhead. Examples of Gen0 would be local variables in methods, temporary objects, or method call arguments that aren’t used later on.</p>
</li>
<li><p><strong>Gen1 and Gen2</strong>: This is for longer-lived objects that survive Gen0 collections, like static objects that are kept alive for the lifetime of the application (that is, singletons), caching objects or large collections used across many operations.</p>
</li>
</ul>
<p>Objects in <strong>Gen0</strong> are collected quickly but often, and objects in <strong>Gen2</strong> are collected infrequently but with more effort because they are larger or more persistent. A lot of <strong>Gen0</strong> collections can be an indicator of inefficient memory usage, while <strong>Gen2 or 3</strong> collections may indicate that your app is keeping too many long-lived objects in memory.</p>
<p><strong>Allocated Column:</strong><br>The <strong>Allocated</strong> column shows the total memory allocated by each method during its execution. This is typically reported in kilobytes (KB).</p>
<p>This information helps you see how memory-intensive each method is, which can impact performance, especially if the method is called frequently.</p>
<p>For example, <code>StringBuilderConcatenation</code> is much more memory-efficient than <code>StringConcatenation</code>, which makes it preferable in cases where memory usage is a concern or where this operation is performed frequently.</p>
<h2 id="heading-what-else-can-you-test-with-benchmarkdotnet">What Else Can You Test with BenchmarkDotnet?</h2>
<h3 id="heading-throughput">Throughput</h3>
<ul>
<li><p>Analyzes how many iterations of a method can be executed per second.</p>
</li>
<li><p>Indicates the efficiency and scalability of the code.</p>
</li>
</ul>
<h3 id="heading-jit-just-in-time-optimization-impact">JIT (Just-In-Time) Optimization Impact</h3>
<ul>
<li><p>Evaluates the effects of JIT optimizations on performance.</p>
</li>
<li><p>Can test cold starts (first-run performance) versus steady-state performance (subsequent runs).</p>
</li>
</ul>
<h3 id="heading-platform-and-framework-differences">Platform and Framework Differences</h3>
<p>You could run benchmarks of the same code across different .NET runtimes (for example, .NET 6, .NET 8, .NET Framework) to compare whether it’s worth upgrading your application to newer systems or not.</p>
<p>Simply update the TargetFramework node in the <code>.csproj</code> file of your application to target the frameworks you wish to test.</p>
<p>Add the following attributes to your benchmark class (based on the target runtime).</p>
<pre><code class="lang-bash">[SimpleJob(runtimeMoniker: RuntimeMoniker.Net60)]
[SimpleJob(runtimeMoniker: RuntimeMoniker.Net80)]
</code></pre>
<p>when you run your application you will get an output as below highlighting the differenes in methods across both .net 6 and .net 8</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Method</td><td>Job</td><td>Runtime</td><td>Mean</td><td>Error</td><td>StdDev</td></tr>
</thead>
<tbody>
<tr>
<td>StringConcatenation</td><td>.NET 6.0</td><td>.NET 6.0</td><td>286.503 us</td><td>3.5004 us</td><td>3.1030 us</td></tr>
<tr>
<td>StringBuilderConcatenation</td><td>.NET 6.0</td><td>.NET 6.0</td><td>4.595 us</td><td>0.0620 us</td><td>0.0580 us</td></tr>
<tr>
<td>StringConcatenation</td><td>.NET 8.0</td><td>.NET 8.0</td><td>222.270 us</td><td>1.7561 us</td><td>1.4664 us</td></tr>
<tr>
<td>StringBuilderConcatenation</td><td>.NET 8.0</td><td>.NET 8.0</td><td>1.650 us</td><td>0.0139 us</td><td>0.0116 us</td></tr>
</tbody>
</table>
</div><h3 id="heading-impact-of-input-parameters">Impact of Input Parameters</h3>
<ul>
<li><p>Supports parameterized benchmarks to test how different inputs affect performance.</p>
</li>
<li><p>Helps identify optimal input ranges or problematic edge cases.</p>
</li>
</ul>
<p>You can do something like this</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> BenchmarkDotNet.Attributes;
<span class="hljs-keyword">using</span> BenchmarkDotNet.Running;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">SortBenchmark</span>
{
    [<span class="hljs-meta">Params(10, 100, 1000)</span>]  <span class="hljs-comment">// Size of the array</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> N;

    [<span class="hljs-meta">Params(10, 100, 1000)</span>]  <span class="hljs-comment">// Maximum value of the array elements</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> MaxValue;

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span>[] data;

    <span class="hljs-comment">// Setup method to create an array before each benchmark</span>
    [<span class="hljs-meta">GlobalSetup</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Setup</span>(<span class="hljs-params"></span>)</span>
    {
        data = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[N];
        <span class="hljs-keyword">var</span> rand = <span class="hljs-keyword">new</span> Random();
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; N; i++)
        {
            data[i] = rand.Next(MaxValue);
        }
    }

    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">SortArray</span>(<span class="hljs-params"></span>)</span>
    {
        Array.Sort(data);  <span class="hljs-comment">// Sort the array</span>
    }
}

<span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
{
    <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"><span class="hljs-keyword">string</span>[] args</span>)</span>
    {
        <span class="hljs-comment">// Run the benchmark</span>
        BenchmarkRunner.Run&lt;SortBenchmark&gt;();
    }
}
</code></pre>
<p>Giving the output of:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Method</td><td>N</td><td>MaxValue</td><td>Mean</td><td>Error</td><td>StdDev</td><td>Allocated</td></tr>
</thead>
<tbody>
<tr>
<td>SortArray</td><td>10</td><td>10</td><td>3.5 ns</td><td>0.1 ns</td><td>0.05 ns</td><td>0 B</td></tr>
<tr>
<td>SortArray</td><td>10</td><td>1000</td><td>4.0 ns</td><td>0.2 ns</td><td>0.1 ns</td><td>0 B</td></tr>
<tr>
<td>SortArray</td><td>100</td><td>10</td><td>20.1 ns</td><td>0.5 ns</td><td>0.3 ns</td><td>0 B</td></tr>
<tr>
<td>SortArray</td><td>100</td><td>1000</td><td>25.2 ns</td><td>0.8 ns</td><td>0.4 ns</td><td>0 B</td></tr>
<tr>
<td>SortArray</td><td>1000</td><td>10</td><td>300.3 ns</td><td>5.6 ns</td><td>2.7 ns</td><td>0 B</td></tr>
<tr>
<td>SortArray</td><td>1000</td><td>1000</td><td>320.1 ns</td><td>6.3 ns</td><td>3.1 ns</td><td>0 B</td></tr>
</tbody>
</table>
</div><h3 id="heading-third-party-library-performance">Third-Party Library Performance</h3>
<p>Using the techniques mentioned above, you can compare the performance of different third-party libraries for the same task to make informed decisions on library usage.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>There you have it, how to benchmark your C# application. Using a combination of these methods, tools and techniques, the possibilities of benchmarking are incredible.</p>
<p>You can use benchmarking to improve your application’s code base, help make decisions on upgrade paths, and method choices.</p>
<p>I hope you find this article helpful, and as always, if you wish to discuss it you can follow me on <a target="_blank" href="https://x.com/grantdotdev">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Simplify Your Git Commands with Git Aliases ]]>
                </title>
                <description>
                    <![CDATA[ As a developer, you probably use the Git CLI (Command Line Interface) daily. However, writing the same old commands repeatedly can be laborious, especially when the commands are lengthy. This is where Git aliases come in to help out. In this article,... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-simplify-your-git-commands-with-git-aliases/</link>
                <guid isPermaLink="false">673342b4d519e9b4d12f23d8</guid>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Tue, 12 Nov 2024 11:57:40 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1730061609849/a3f3e8f3-102e-4dde-bec7-660be0121fad.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>As a developer, you probably use the Git CLI (Command Line Interface) daily. However, writing the same old commands repeatedly can be laborious, especially when the commands are lengthy. This is where Git aliases come in to help out.</p>
<p>In this article, you’ll learn how to simplify your Git commands by using aliases.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-are-git-aliases">What Are Git Aliases?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-git-aliases-via-the-global-git-configuration-file-recommended">How to Add Git Aliases Via the Global Git Configuration File (Recommended)</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-to-set-your-preferred-git-editor">How to Set Your Preferred Git Editor</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-open-the-git-config-file">How to Open the Git Config File</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-a-git-alias-via-your-config-file">How to Add a Git Alias Via Your Config File</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-aliases-in-the-cli">How to Add Aliases in the CLI</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-custom-commands-for-more-complex-shortcuts">How to Create Custom Commands for More Complex Shortcuts</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-how-to-use-parameters-in-all-commands">How to Use Parameters in All Commands</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-other-useful-aliases">Other Useful Aliases</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary">Summary</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Knowledge of Git.</p>
</li>
<li><p>Git Bash installed (optional but recommended Windows users).</p>
</li>
<li><p>An IDE like VS Code (this is also optional).</p>
</li>
</ul>
<h2 id="heading-what-are-git-aliases">What Are Git Aliases?</h2>
<p>Git aliases are custom shortcuts for existing Git commands, making common tasks quicker and easier. They let you define your commands, allowing you to tailor shortcuts exactly how you want.</p>
<p>You have two main options for adding/creating git aliases in your git configuration, using your Git configuration file or adding them directly via the CLI (terminal/command line).</p>
<h2 id="heading-how-to-add-git-aliases-via-the-global-git-configuration-file-recommended">How to Add Git Aliases Via the Global Git Configuration File (Recommended)</h2>
<p>This option involves opening your global git config file and appending your git aliases to the bottom of the file.</p>
<h3 id="heading-how-to-set-your-preferred-git-editor">How to Set Your Preferred Git Editor</h3>
<p>Set your default Git config editor software, for example, I use VS Code to edit my Git configuration file, but you can use whatever text editor/code editor you prefer.</p>
<p>Run this command to set Notepad as your preferred editor on Windows (CMD/PowerShell):</p>
<pre><code class="lang-bash">git config --global core.editor <span class="hljs-string">"notepad"</span>
</code></pre>
<p>Run this command to set VS Code as your preferred editor on Windows &amp; MacOS /Linux:</p>
<pre><code class="lang-bash">git config --global core.editor <span class="hljs-string">"code --wait"</span>
</code></pre>
<p>To set a different default editor, search online for “Set {editor} as default Git editor,” and replace <code>{editor}</code> with your preferred app.</p>
<h3 id="heading-how-to-open-the-git-config-file">How to Open the Git Config File</h3>
<p>Open your terminal of choice and enter the following command. This will open the global Git config file (<code>git config —global</code>), in edit mode (<code>-e</code>).</p>
<pre><code class="lang-bash">git config --global -e
</code></pre>
<p>You can open the git configuration file directly from the following locations:</p>
<p><strong>Mac Os</strong>: Home Directory → show hidden (Cmd + Shift + H) → <code>.gitconfig</code>  </p>
<p><strong>Windows</strong>: <code>C:\Users\YourUsername\</code> → then show hidden files (in View) → and find <code>.gitconfig</code>  </p>
<p><strong>Linux:</strong> Home Directory → show hidden (Ctrl + H) → <code>.gitconfig</code></p>
<h3 id="heading-how-to-add-a-git-alias-via-your-config-file">How to Add a Git Alias Via Your Config File</h3>
<p>If you're adding Git aliases for the first time, open your <code>.gitconfig</code> file, add <code>[alias]</code> at the end, and then list your shortcuts below. This tells Git these are aliases. Add your preferred alias (the shortened command you wish to run).</p>
<p>The format of a git alias is <code>&lt;alias&gt; = &lt;command&gt;</code>, so we have:</p>
<pre><code class="lang-bash">co = checkout
cob = checkout -b
</code></pre>
<p><strong>Explanation of the above examples:</strong></p>
<p><code>co = checkout</code> this maps the <code>git checkout</code> command to a shorter <code>git co</code> command. You’d then call <code>git co feature/123</code> in your terminal.</p>
<p>You do not need to type <code>git</code> in front of the command, as the configuration will pre-pend this automatically as it knows the command you’re mapping is a Git command.  </p>
<p><strong>Note</strong>: Any parameters passed to the command will be applied to the final command called within the alias only.  </p>
<p>More aliases can be added in this way, mapping shortcuts to existing git commands. Saving and closing the file will then make the aliases available within your terminal.</p>
<h2 id="heading-how-to-add-aliases-in-the-cli">How to Add Aliases in the CLI</h2>
<p>If you want a more streamlined approach to adding Git aliases, you can add them directly from within the terminal/command line.</p>
<p>Taking the examples above, we can add these directly in the following way:</p>
<p>The format of the command is: <code>git config --global alias.{alias} "{original command}"</code>:</p>
<pre><code class="lang-bash">git config --global alias.co <span class="hljs-string">"checkout"</span>
<span class="hljs-comment">#or</span>
git config --global alias.cob <span class="hljs-string">"checkout -b"</span>
</code></pre>
<p>It’s as easy as that!</p>
<h2 id="heading-how-to-create-custom-commands-for-more-complex-shortcuts">How to Create Custom Commands for More Complex Shortcuts</h2>
<p>Ok, this seems great, but it’s not really that impressive – we’re only removing a few characters. However, we can make them much more helpful, we can create our commands using shell commands.</p>
<p>Let’s take the following example, a command I use a lot!</p>
<pre><code class="lang-bash">new-work = !git checkout main &amp;&amp; git pull &amp;&amp; git cob
</code></pre>
<p>This alias combines multiple Git commands into one shell command. The <code>!</code> character tells Git to treat it as a shell command, not a standard Git command.  </p>
<p>Without <code>!</code>, Git treats the alias as a Git command (for example, <code>checkout</code> becomes <code>git checkout</code>). With <code>!</code>, Git knows to run it as a shell command without adding <code>git</code> in front.</p>
<p>By chaining these commands, we can write much more useful aliases. The one above will:</p>
<ul>
<li><p>First, check out the <code>main</code> branch.</p>
</li>
<li><p>Using the <code>&amp;&amp;</code> operator, it means the other commands will only run if the previous one has been successful.</p>
</li>
<li><p>Secondly, it will pull down the changes from <code>main</code>.</p>
</li>
<li><p>Finally, create a new branch from the <code>main</code> branch using our other alias <code>git cob</code>.</p>
</li>
</ul>
<p>The final command can then accept parameters (as the original Git command would), so it can be used like so:</p>
<pre><code class="lang-bash">git new-work <span class="hljs-string">'feature/new-work-from-main'</span>
</code></pre>
<h3 id="heading-how-to-use-parameters-in-all-commands">How to Use Parameters in All Commands</h3>
<p>Up until now, we’ve only been able to pass our parameters to the final git command in our alias. However, what if we want to pass parameters to some, if not all of the commands within the alias? We can achieve this by using a shell function.</p>
<p>Take the following example:</p>
<pre><code class="lang-bash">new-work = <span class="hljs-string">"!f() { git checkout \"<span class="hljs-variable">$1</span>\" &amp;&amp; git pull &amp;&amp; git checkout -b \"<span class="hljs-variable">$2</span>\"; }; f"</span>
</code></pre>
<p>Above we’re using a shell function that processes input parameters.</p>
<p><strong>Explanation:</strong></p>
<ol>
<li><p><code>!f()</code>:</p>
<ul>
<li><p>The <code>!</code> tells Git to interpret the alias as a shell command rather than a standard Git command.</p>
</li>
<li><p><code>f()</code> defines a shell function <code>f</code> that will allow us to execute multiple commands in sequence.</p>
</li>
</ul>
</li>
<li><p>Everything inside <code>{ }</code> is what will be executed within the <code>f()</code> function.</p>
</li>
<li><p><code>git checkout \”$1”'\</code>: Will run a parameterized Git command, where <code>$1</code> is escaped and will be replaced with the 1st parameter passed to the alias. The <code>\"</code> escape sequences around <code>$1</code> allow for branch names with spaces.</p>
</li>
<li><p><code>&amp;&amp;</code> is a logical operator that ensures each command only runs if the previous one succeeds. If <code>git checkout "$1"</code> fails, the commands that follow won’t run.</p>
</li>
<li><p><code>git checkout -b \”$2”\</code> : Creates a new branch with the name of the second parameter as before.</p>
</li>
<li><p><code>;</code>: Marks the end of the <code>f()</code> function;</p>
</li>
<li><p><code>f</code>: The final <code>f</code> calls the alias function immediately, meaning that when you call the alias, it declares the function and then calls it immediately.</p>
</li>
</ol>
<p><strong>Usage:</strong></p>
<pre><code class="lang-bash">git new-work development task/feat-123
</code></pre>
<h2 id="heading-other-useful-aliases">Other Useful Aliases</h2>
<pre><code class="lang-bash">[<span class="hljs-built_in">alias</span>]
     co = checkout
    cob = checkout -b
    s = status
    tidy-up = !git checkout main &amp;&amp; git branch | grep -v <span class="hljs-string">"main"</span> | xargs git branch -D
    latest = !git checkout main &amp;&amp; git pull
    new-work = <span class="hljs-string">"!f() { git checkout \"<span class="hljs-variable">$1</span>\" &amp;&amp; git pull &amp;&amp; git checkout -b \"<span class="hljs-variable">$2</span>\"; }; f"</span>
    <span class="hljs-keyword">done</span> = !git push -u origin HEAD
    save = !git add -A &amp;&amp; git commit
    saveM = !git add -A &amp;&amp; git commit -m
    br = branch --format=<span class="hljs-string">'%(HEAD) %(color:yellow)%(refname:short)%(color:reset) - %(contents:subject) %(color:green)(%(committerdate:relative)) [%(authorname)]'</span> --sort=-committerdate
</code></pre>
<h2 id="heading-summary">Summary</h2>
<p><code>co:</code> Checkout given branch → <code>git co task/feat-123</code>  </p>
<p><code>cob</code>: Creates a new branch from the current branch → <code>git cob feature/123</code>  </p>
<p><code>s</code>: Calls <code>git status</code> to view the status of the current git branch → <code>git s</code>  </p>
<p><code>tidy-up</code>: Deletes all local branches other than <code>main</code> → <code>git tidy-up</code>  </p>
<p><code>latest</code>: Gets the latest changes from remote <code>main</code> branch → <code>git latest</code>  </p>
<p><code>new-work</code>: Creates a new branch (2nd param) from 1st param branch → <code>git new-work main feat/123</code></p>
<p><code>git done</code>: Pushes the current branch to the remote repository (<code>origin</code>) and sets it as the upstream branch. This can be helpful when pushing your first commit and you get the error:<br><code>fatal: The current branch has no upstream branch. To push the current branch and set the remote as upstream, use git push --set-upstream origin</code></p>
<p><code>save</code>: Will simply add all changed files, and commit them, opening your default Git editor and requesting a commit message → <code>git save</code></p>
<p><code>savem</code>: Will do as above, but instead of opening your editor, you can pass in a commit message inline → <code>git savem ‘Task123: add index.html</code></p>
<p><code>br:</code> This one looks complicated, but it’s not as complicated as it seems but does highlight the power of aliases. In essence, it customizes the output format of <code>git branch</code> to display a detailed, color-coded list of branches, sorted by the most recent commit date, it will look something like the image below for each branch you have locally.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730060113591/36008ee8-e54e-4b06-8a84-2a20885a1255.png" alt="36008ee8-e54e-4b06-8a84-2a20885a1255" class="image--center mx-auto" width="1252" height="52" loading="lazy"></p>
<p>There you have it, an introduction to Git aliases and some useful examples of aliases you can add as a starter to your configuration.</p>
<p>As always if you want to chat about it, or hear about future articles you can follow me on <a target="_blank" href="https://x.com/grantdotdev">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Write Extension Methods in C# ]]>
                </title>
                <description>
                    <![CDATA[ Extension methods are a fundamental part of C# and Object Oriented Programming (OOP). Extension methods in C# allow you to "extend" existing types, including classes, interfaces, or structs, without modifying their original code. This is particularly... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-write-extension-methods-in-csharp/</link>
                <guid isPermaLink="false">6722f316b717c1a9c6f35b34</guid>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Thu, 31 Oct 2024 03:01:42 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1730322390431/42cfe64d-0fce-4f36-a4f7-25e1e56267a4.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Extension methods are a fundamental part of C# and Object Oriented Programming (OOP). Extension methods in C# allow you to "extend" existing types, including classes, interfaces, or structs, without modifying their original code.</p>
<p>This is particularly useful when you want to add new functionality to a type you don't own or can't change, like types from third-party libraries or built-in .NET types such as <code>string</code>, <code>List&lt;T&gt;</code>, and so on.</p>
<p>In this article, you’ll learn how to add extension methods to your classes, as well as third-party and system classes.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-to-create-datetime-extension-methods">How to Create DateTime Extension Methods</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-chain-same-type-extension-methods">How to Chain Same-Type Extension Methods</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-cant-i-just-add-these-methods-to-my-class">Why Can’t I Just Add These Methods to My Class?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-to-use-extensions">When to Use Extensions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-not-to-use-extension-methods">When Not to Use Extension Methods</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-things-to-consider-when-designing-extensions">Things to Consider When Designing Extensions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-how-to-create-datetime-extension-methods">How to Create DateTime Extension Methods</h2>
<p>Let’s say we want some methods that can be used along with the existing <code>DateTime</code> class, perhaps a method that returns whether the given <code>DateTime</code> object is a weekend or something different.</p>
<p>Extension methods need to be defined in a static class because they are essentially syntactic sugar that allows you to call a static method as if it were an instance method on the type you're extending.</p>
<p>Extension methods need to be in a static class because:</p>
<ol>
<li><p><strong>No Object Needed:</strong> You don’t need to create an object to use an extension method. Since the method adds new functionality to an existing type (like <code>string</code>), it can work without needing an instance of the class.</p>
</li>
<li><p><strong>Organized Code:</strong> Putting extension methods in a static class keeps things tidy. It allows you to group related methods, and you can easily include them in your code by using the appropriate namespace.</p>
</li>
</ol>
<p>So, by using a static class, you can add useful methods to existing types without changing their original code, and you don’t need an object to call them.</p>
<p>First, let’s create a <code>DateTimeExtensions</code> static class.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">DateTimeExtensions</span> {

}
</code></pre>
<p>This will encompass all the <code>DateTime</code> extensions we want to create.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsWeekend</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> DateTime date</span>)</span>
{
    <span class="hljs-keyword">return</span> date.DayOfWeek <span class="hljs-keyword">is</span> DayOfWeek.Saturday or DayOfWeek.Sunday;
}
</code></pre>
<p><strong>Explanation:</strong></p>
<p><code>public static bool IsWeekend</code>: This defines that it is a static method called <code>IsWeekend</code> which will return a <code>bool</code> value (true/false).</p>
<p><code>this DateTime date</code>: The <code>this</code> keyword as a method argument denotes that this method is an extension method. It means that the method will be an extension of the <code>DateTime</code> class.</p>
<h2 id="heading-how-to-chain-same-type-extension-methods">How to Chain Same-Type Extension Methods</h2>
<p>For an extension method to be chained with others, it typically needs to return the same type as the one it's extending (or a compatible type). This allows another method to be called on the result of the previous one.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> System.Globalization;

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">ToTitleCase</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> <span class="hljs-keyword">string</span> str</span>)</span>
{
    <span class="hljs-keyword">return</span> CultureInfo.CurrentCulture.TextInfo.ToTitleCase(str.ToLower());
}

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">TrimAndAppend</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> <span class="hljs-keyword">string</span> str, <span class="hljs-keyword">string</span> toAppend</span>)</span>
{
    <span class="hljs-keyword">return</span> str.Trim() + toAppend;
}
</code></pre>
<p>In the above example, both the <code>ToTitleCase</code> and <code>TrimAndAppend</code> methods return a string value, meaning that we can chain the extension methods as below, which will convert the string to title-case before trimming all whitespace and appending the provided string.</p>
<p>Notice that we only provided the second parameter to the <code>TrimAndAppend</code> method, as the first parameter is the string having the extension method applied to (as explained previously, denoted by the <code>this</code> keyword).</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> title = <span class="hljs-string">"hello world   "</span>
    .ToTitleCase()
    .TrimAndAppend(<span class="hljs-string">"!!"</span>);

<span class="hljs-comment">//Output:</span>
<span class="hljs-comment">// Hello World!!</span>
</code></pre>
<p>If the extension method returns a different type (not the original one or a compatible type), you cannot chain it. For example:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> date = <span class="hljs-keyword">new</span> DateTime();
date.IsWeekend().AddDays(<span class="hljs-number">1</span>);
</code></pre>
<p>For less obvious reasons, this will not work. When you chain methods, they do not chain from the original variable—they chain from the return type of the previous method call.  </p>
<p>Here, we have a date called <code>IsWeekend()</code> which returns a Boolean. We then attempted to call <code>AddDays(1)</code> on a Boolean value which doesn’t exist, as it is a <code>DateTime</code> extension. The code compiler will fail to build, raising an error informing you of this.</p>
<h3 id="heading-how-to-return-the-instance-to-chain">How to Return the Instance to Chain</h3>
<p>In some extension methods, especially those for configuration (like Dependency Injection), you return the same instance to allow method chaining. This lets you continue working with the original object or its modified state across multiple calls, enabling a fluent interface.</p>
<p>Let’s take the example of a list of cars.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-title">List</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-title">RemoveDuplicates</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params"><span class="hljs-keyword">this</span> List&lt;T&gt; list</span>)</span>
{
    <span class="hljs-comment">// Use Distinct to remove duplicates and update the list</span>
    list = list.Distinct().ToList();

    <span class="hljs-comment">// Return the modified list to allow method chaining</span>
    <span class="hljs-keyword">return</span> list;
}

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-title">List</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-title">AddRangeOfItems</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params"><span class="hljs-keyword">this</span> List&lt;T&gt; list, IEnumerable&lt;T&gt; items</span>)</span>
{
    <span class="hljs-comment">// Add a range of items to the list</span>
    list.AddRange(items);

    <span class="hljs-comment">// Return the modified list to allow method chaining</span>
    <span class="hljs-keyword">return</span> list;  
}
</code></pre>
<p>Now that we’ve returned the list from these extension methods, we can chain additional methods on the same list. For example, after removing duplicates with <code>RemoveDuplicates()</code>, we can immediately call <code>AddRangeOfItems()</code> on the same list.</p>
<p>So we can do something like:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> existingStock = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt; { <span class="hljs-string">"Ford"</span>, <span class="hljs-string">"Jaguar"</span>, <span class="hljs-string">"Ferrari"</span>, <span class="hljs-string">"Ford"</span>, <span class="hljs-string">"Renault"</span> };

<span class="hljs-keyword">var</span> availableBrands = existingStock
    .RemoveDuplicates()
    .AddRangeOfItems(<span class="hljs-keyword">new</span>[] { <span class="hljs-string">"Lamborghini"</span> }); <span class="hljs-comment">// new stock available</span>

Console.WriteLine(<span class="hljs-string">"Brands Available Now: "</span> + <span class="hljs-keyword">string</span>.Join(<span class="hljs-string">", "</span>, availableBrands));

<span class="hljs-comment">// Output: Brands Available Now: Ford, Jaguar, Ferrari, Renault, Lamborghini</span>
</code></pre>
<p>We removed duplicates from a list of car brands and added new stock to the same list. This works because <code>RemoveDuplicates</code> returns the list, enabling us to chain it with <code>AddRangeOfItems</code>.</p>
<p>If <code>RemoveDuplicates</code> returned <code>void</code> instead of the list, we wouldn't be able to chain the methods. It would still remove duplicates, but further actions like adding new stock, wouldn't be possible in the same expression.</p>
<p>We’d also have to update the <code>RemoveDuplicates</code> to update the list argument passed in, as <code>Distinct()</code> returns a new list which isn’t being returned as shown below, which I think you’ll agree is a lot more verbose.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">RemoveDuplicates</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params"><span class="hljs-keyword">this</span> List&lt;T&gt; list</span>)</span>
{
    <span class="hljs-comment">// Get the distinct elements and clear the original list</span>
    <span class="hljs-keyword">var</span> distinctItems = list.Distinct().ToList();
    list.Clear(); 

    <span class="hljs-comment">// Add the distinct items back to the original list</span>
    list.AddRange(distinctItems);
}
</code></pre>
<h2 id="heading-why-cant-i-just-add-these-methods-to-my-class">Why Can’t I Just Add These Methods to My Class?</h2>
<p>If the method is not a core part of the class’s functionality, placing it in an extension method can help keep the class focused and maintainable.</p>
<p><strong>Separation of Concerns</strong>: Using extension methods keeps your code cleaner, and helps reduce complexity<strong>.</strong> It helps avoid bloating the class with methods that may not be used frequently.</p>
<p><strong>Enhancing External Libraries</strong>: If you’re using a library or framework where you cannot modify the source code, extension methods allow you to add functionality to those types without altering their definitions.</p>
<p>Let’s say you’re using the <code>FileInfo</code> class from the <a target="_blank" href="http://System.IO"><code>System.IO</code></a> namespace to work with files. You may want to add a method to easily check if a file is too large (for example, more than 1 GB), but you cannot modify the <code>FileInfo</code> class directly because it belongs to the System.IO namespace (that is, it's baked into .Net).</p>
<p><strong>Without an extension:</strong></p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> fileInfo = <span class="hljs-keyword">new</span> FileInfo(<span class="hljs-string">"myFile.txt"</span>);

<span class="hljs-keyword">if</span> (fileInfo.Length &gt; <span class="hljs-number">1024</span> * <span class="hljs-number">1024</span> * <span class="hljs-number">1024</span>) <span class="hljs-comment">// filesize is bigger than 1GB</span>
{
    Console.WriteLine(<span class="hljs-string">"The file is too large."</span>);
}
<span class="hljs-keyword">else</span>
{
    Console.WriteLine(<span class="hljs-string">"The file size is acceptable."</span>);
}
</code></pre>
<p><strong>With Extension Method:</strong></p>
<p>You can make this more reusable by adding an extension method that checks whether the file is larger than 1 GB.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">FileInfoExtensions</span>
{
    <span class="hljs-comment">//extension method, with default file size of 1GB (can be overriden)</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">IsFileTooLarge</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> FileInfo fileInfo, <span class="hljs-keyword">long</span> sizeInBytes = <span class="hljs-number">1024</span> * <span class="hljs-number">1024</span> * <span class="hljs-number">1024</span></span>)</span>
    {
        <span class="hljs-keyword">return</span> fileInfo.Length &gt; sizeInBytes;
    }
}
</code></pre>
<p>Now you can use the <code>IsFileTooLarge</code> method directly on <code>FileInfo</code> objects, making your code cleaner:</p>
<pre><code class="lang-csharp">csharpCopy codevar fileInfo = <span class="hljs-keyword">new</span> FileInfo(<span class="hljs-string">"myFile.txt"</span>);

<span class="hljs-keyword">if</span> (fileInfo.IsFileTooLarge())
{
    Console.WriteLine(<span class="hljs-string">"The file is too large."</span>);
}
<span class="hljs-keyword">else</span>
{
    Console.WriteLine(<span class="hljs-string">"The file size is acceptable."</span>);
}
</code></pre>
<p>Extending third-party libraries and packages can make your code much more compatible.</p>
<p><strong>Better Organization &amp; Readability</strong>: You can organize extension methods into static classes based on functionality or context, making it easier to find and use them. This is certainly enhanced by allowing extension methods to be chained.</p>
<h2 id="heading-when-to-use-extensions">When to Use Extensions</h2>
<ul>
<li><p><strong>For Utility Methods:</strong> If you have utility methods that are useful for a type but don’t belong directly in the type itself (for example, formatting, validation).</p>
</li>
<li><p><strong>For Enhancing Built-In Types:</strong> If you want to add functionality to built-in types (like <code>string</code> or <code>DateTime</code>) without modifying them.</p>
</li>
<li><p><strong>When You Want to Keep Methods Optional:</strong> If you want to provide additional methods that users can opt to use without forcing them to incorporate them into the main class design.</p>
</li>
</ul>
<h3 id="heading-example-scenario"><strong>Example Scenario</strong></h3>
<p>Imagine that you have a <code>Person</code> class, and you want to add a method to format the person's name nicely:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Person</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> FirstName { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> LastName { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}

<span class="hljs-comment">// Extension method in a static class</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">PersonExtensions</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">GetFullName</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> Person person</span>)</span>
    {
        <span class="hljs-keyword">return</span> <span class="hljs-string">$"<span class="hljs-subst">{person.FirstName}</span> <span class="hljs-subst">{person.LastName}</span>"</span>;
    }
}
</code></pre>
<p>By using an extension method for <code>GetFullName</code>, you can keep the <code>Person</code> class simple and focused on its core responsibilities, while still providing useful functionality.</p>
<h2 id="heading-when-not-to-use-extension-methods">When Not to Use Extension Methods</h2>
<ul>
<li><p><strong>For Core Functionality:</strong> If a method is essential to a class's core behavior, it should be part of the class itself, not an extension.</p>
</li>
<li><p><strong>For Tight Coupling:</strong> If the extension method requires intimate knowledge of the class's private state or needs regular access to its internal logic.</p>
</li>
<li><p><strong>For Public APIs:</strong> When designing a public-facing library or API, it's often better to include necessary methods directly in the class rather than forcing users to find or create their extension methods.</p>
</li>
</ul>
<h2 id="heading-things-to-consider-when-designing-extensions">Things to Consider When Designing Extensions</h2>
<p>While extension methods are powerful and convenient in many cases, there are certain cons or situations where using them might not be the best choice:</p>
<h3 id="heading-hidden-behaviorconfusion">Hidden Behavior/Confusion</h3>
<ul>
<li><p>Extension methods don't appear directly in the class definition, meaning that they can be harder to discover by developers who are unfamiliar with the available extensions.</p>
</li>
<li><p>Developers need to know that these extension methods exist, or they might miss using them unless they are working in an IDE with features like IntelliSense (for example, Visual Studio, JetBrains Rider). These IDEs can suggest extension methods from other files or namespaces as they detect the appropriate type. Without a feature-rich IDE, the developer would have to be aware of the extension methods or find the folder they’re stored in.</p>
</li>
</ul>
<h3 id="heading-cant-access-private-members"><strong>Can’t Access Private Members</strong></h3>
<ul>
<li><p>Extension methods can only access members (methods, properties, fields) that are public or internal.</p>
</li>
<li><p>They cannot access private or protected members of a class because extension methods operate as if they are part of the class from the outside, similar to regular method calls from outside the class.</p>
</li>
</ul>
<p><strong>Example:</strong></p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Car</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span> engineNumber = <span class="hljs-string">"12345"</span>; <span class="hljs-comment">// Private field</span>

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Brand { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } = <span class="hljs-string">"Ford"</span>; <span class="hljs-comment">// Public property</span>

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">StartEngine</span>(<span class="hljs-params"></span>) <span class="hljs-comment">// Private method</span></span>
    {
        Console.WriteLine(<span class="hljs-string">"Engine started"</span>);
    }
}
</code></pre>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">CarExtensions</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">DisplayBrand</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> Car car</span>)</span>
    {
        Console.WriteLine(<span class="hljs-string">$"Brand: <span class="hljs-subst">{car.Brand}</span>"</span>); <span class="hljs-comment">// Accessing the public 'Brand' property</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">TryAccessPrivateField</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> Car car</span>)</span>
    {
        <span class="hljs-comment">// Cannot access the private 'engineNumber'</span>
        <span class="hljs-comment">// This will result in a compile-time error.</span>
        Console.WriteLine(car.engineNumber);
    }
}
</code></pre>
<h3 id="heading-code-duplication-amp-overuse"><strong>Code Duplication &amp; Overuse</strong></h3>
<ul>
<li><p>In some cases, extension methods can encourage code duplication. If multiple projects or classes require similar extension methods, you might end up writing or copying the same extension methods in different places, making it harder to manage and update the code consistently.  </p>
<p>  To avoid this, organize your code effectively. I would recommend keeping all extensions within an extensions folder or project, close to the origin (depending on the design patterns being used within your application).</p>
</li>
</ul>
<ul>
<li><strong>Abuse of Extensions:</strong> If used excessively, they can clutter the global space with methods that might not need to be global. This can cause pollution of the type’s API, making it harder to understand what’s core to the class versus what’s added via extensions.</li>
</ul>
<p>    In some cases, it's better to encapsulate functionality in a separate helper classes or services rather than adding it via extension methods.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Extension methods are useful for adding functionality in a clean and modular way, but they can also introduce confusion, namespace conflicts, and lack of access to private members.</p>
<p>As highlighted throughout the article, they have many usages and are certainly a very nice feature of the Dotnet framework when used effectively. They should be used when appropriate, but not as a substitute for functionality that belongs within the class itself.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Conditional Statements in C#: If, Switch, and More Explained with Example Code ]]>
                </title>
                <description>
                    <![CDATA[ Being able to update variables, call particular branches of code, or simply output different code based on a certain condition is a vital part of programming in any language. C# (C Sharp) offers multiple ways to do these things, and I’m about to show... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/conditional-statements-in-csharp-if-switch-and-more/</link>
                <guid isPermaLink="false">67169d1456af142bfd7fe418</guid>
                
                    <category>
                        <![CDATA[ Conditionals ]]>
                    </category>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Mon, 21 Oct 2024 18:27:32 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729432206658/e802fcca-bf0c-424f-9915-b02fd847bdcc.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Being able to update variables, call particular branches of code, or simply output different code based on a certain condition is a vital part of programming in any language.</p>
<p>C# (C Sharp) offers multiple ways to do these things, and I’m about to show you some of the most common ones. We’ll discuss the pros and cons of each method, and which ones are more suited for particular scenarios.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-you-will-learn-in-this-article">What You Will Learn In This Article</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-if-else-if-else-statements">If / Else if / Else Statements</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-replacing-if-else-with-ternary-operator">Replacing If / Else with Ternary Operator</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-switch-case-statements">Switch Case Statements</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-switch-statements-expression-syntax">Switch Statements - Expression Syntax</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-performance-summary">Performance Summary</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-you-will-learn-in-this-article">What You Will Learn In This Article</h2>
<p>I will teach you all about the following conditional coding mechanisms within C#.</p>
<ul>
<li><p>If / Else If / Else statements</p>
</li>
<li><p>Ternary statements</p>
</li>
<li><p>Switch-Case statements</p>
</li>
<li><p>Analysing performance of conditional coding options</p>
</li>
</ul>
<p><strong>Pre-requisites:</strong></p>
<ul>
<li><p>Very basic knowledge of the C# coding language</p>
</li>
<li><p>Coding IDE (to code along if you wish)</p>
</li>
</ul>
<h2 id="heading-if-else-if-else-statements">If / Else if / Else Statements</h2>
<p>When first learning a new programming language, the <code>If</code> statement is a staple in any developer’s learning syllabus. It’s the easiest way in which you can conditionally route the flow of your code’s execution.</p>
<p>Let’s take a look at building up the complexity of the the <code>If</code> statement syntax. In its most basic form, you can use it as a simple <code>if</code> clause, meaning the code within the <code>if</code> block will only be executed if a condition is met.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-csharp"><span class="hljs-comment">// For the demo we're using a hard coded age.</span>
<span class="hljs-keyword">int</span> age = <span class="hljs-number">22</span>;

<span class="hljs-comment">//check person is of legal age in the UK</span>
<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">string</span>.IsNullOrEmpty(age) &amp;&amp; age &gt;= <span class="hljs-number">18</span>)
{
   <span class="hljs-comment">// If true, run the following code</span>
   AllowAccessToNightClub();
}
</code></pre>
<p>Going one step further, if we wanted to carry out some code where the statement results in <code>false</code>, we can do that using an <code>if / else</code> statement like so:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> ageLimit = <span class="hljs-number">18</span>;
<span class="hljs-keyword">var</span> customersAge = <span class="hljs-number">17</span>

<span class="hljs-keyword">if</span>(customersAge &gt;= ageLimit){
{   
    AllowAccessToTheClub();
} <span class="hljs-keyword">else</span>{
    <span class="hljs-comment">// Deny Access</span>
    DenyAccess();
    <span class="hljs-comment">// we can also nest if statements</span>
    <span class="hljs-comment">// if the customer doesn't leave call the Police!</span>
    <span class="hljs-keyword">if</span>(!CustomerLeaves()){
        CallThePolice();
    };
}
</code></pre>
<p>The above example illustrates how you can use <code>if / else</code> statements to control your flow of code. You’ve seen how you can handle specific scenarios with nested statements, allowing you to check future outcomes.</p>
<p>In the above code, we are checking if <code>CustomerLeaves()</code> returns a true or false value, and depending on the outcome, we see whether we need to <code>CallThePolice()</code> or not.</p>
<p>For situations where you want to check more than one condition before defaulting to <code>else</code>, you can use the <code>if / else if / else</code> syntax. Let's see how it works:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">int</span> age = <span class="hljs-number">17</span>;

<span class="hljs-keyword">if</span>(age &gt;= <span class="hljs-number">18</span>){
    Console.WriteLine(<span class="hljs-string">"You can drink alcohol in the UK"</span>);
    Console.WriteLine(<span class="hljs-string">"You can vote in the UK"</span>);
    Console.WriteLine(<span class="hljs-string">"You can get a tattoo"</span>);
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(age &gt;= <span class="hljs-number">16</span> &amp;&amp; age &lt; <span class="hljs-number">18</span>){
    Console.WriteLine(<span class="hljs-string">"You can join the royal forces in the UK"</span>);
} <span class="hljs-keyword">else</span>{
   Console.WriteLine(<span class="hljs-string">"Stay in School!"</span>);
}
</code></pre>
<p>The example above uses <code>if / else if / else</code> to display different text based on which condition is met. Only one block will ever run, as you can only match a single condition with this syntax.</p>
<p>Let’s say you wanted to build a stringup based on multiple conditions, but you wanted all conditions to be checked individually. In that case, you could utilise multiple <code>if statements</code> to accomplish this. Be aware, though, that this can make your code less performant and harder to read.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> System.Text;

<span class="hljs-keyword">int</span> age = <span class="hljs-number">20</span>;
<span class="hljs-keyword">bool</span> hasDrivingLicense = <span class="hljs-literal">true</span>;
<span class="hljs-keyword">bool</span> hasVoterID = <span class="hljs-literal">false</span>;

<span class="hljs-keyword">var</span> builder = <span class="hljs-keyword">new</span> StringBuilder();

<span class="hljs-comment">// Multiple independent if statements</span>
<span class="hljs-keyword">if</span> (age &gt;= <span class="hljs-number">18</span>)
{
    builder.Append(<span class="hljs-string">"You are an adult."</span>);
}

<span class="hljs-keyword">if</span> (hasDrivingLicense)
{
    builder.Append(<span class="hljs-string">"You can drive."</span>);
}

<span class="hljs-keyword">if</span> (hasVoterID)
{
    builder.Append(<span class="hljs-string">"You are eligible to vote."</span>);
}

Console.WriteLine(builder.ToString());
Console.WriteLine(builder.ToString());
<span class="hljs-comment">/* Output
You are an adult.You can drive.
*/</span>
</code></pre>
<p>The above code demonstrates using multiple <code>if</code> statements instead of <code>if</code>, <code>else if</code>, <code>else</code>. This allows multiple conditions to be checked independently, so multiple code blocks can run if the conditions are met. This is useful when wanting to assign values to an object’s properties based on different criteria or independent conditions.</p>
<h2 id="heading-replacing-if-else-with-ternary-operator">Replacing If / Else with Ternary Operator</h2>
<p><code>If / Else</code> statements are very useful, but sometimes they can take up a lot of space for really simple assignment code. The <strong>ternary operator</strong> is perfect for these situations. Its syntax uses <code>?</code> and <code>:</code> to represent what happens if the condition is true or false, eliminating the need for brackets.</p>
<p>Here’s an example:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> backgroundColor = isDarkMode ? <span class="hljs-string">"black"</span> : <span class="hljs-string">"white"</span>;
</code></pre>
<p><strong>Explanation:</strong></p>
<ul>
<li><code>backgroundColor</code>: The variable being assigned.</li>
</ul>
<ul>
<li><p><code>isDarkMode</code>: A boolean condition being evaluated.</p>
</li>
<li><p><code>?</code>: Marks the start of the ternary operator. If <code>isDarkMode</code> is <code>true</code>, the value after <code>?</code> ("black") is assigned.</p>
</li>
<li><p><code>:</code>: Separates the true case from the false case. If <code>isDarkMode</code> is <code>false</code>, the value after <code>:</code> ("white") is assigned.</p>
</li>
</ul>
<p>The ternary statement is shorthand for simple conditional assignments, making your code more compact. I would recommend using it for simple variable assignment or simple binary calling of functions (like call A or call B).</p>
<p>Here’s an example:</p>
<pre><code class="lang-csharp">isLoggedIn ? ShowWelcomeMessage() : PromptLogin();

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">ShowWelcomeMessage</span>(<span class="hljs-params"></span>)</span>
{
    Console.WriteLine(<span class="hljs-string">"Welcome back, user!"</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">PromptLogin</span>(<span class="hljs-params"></span>)</span>
{
    Console.WriteLine(<span class="hljs-string">"Please log in to continue."</span>);
}
</code></pre>
<p>Ternary operations can be nested also like so:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> isDarkMode = <span class="hljs-literal">true</span>;
<span class="hljs-keyword">var</span> isAccessibilityActive = <span class="hljs-literal">true</span>;

<span class="hljs-keyword">var</span> backgroundColor = isDarkMode ? isAccessibilityActive ? <span class="hljs-string">"green"</span> : <span class="hljs-string">"black"</span> : <span class="hljs-string">"white"</span>;

Console.WriteLine(backgroundColor);
</code></pre>
<p>The above example uses nested ternary operators to replace a nested if / else statement. Just keep in mind that the flow is different from a regular ternary operation. It would read like this:</p>
<pre><code class="lang-csharp">IF isDarkMode <span class="hljs-keyword">is</span> <span class="hljs-literal">true</span>, then check <span class="hljs-keyword">if</span> isAccessibilityActive <span class="hljs-keyword">is</span> <span class="hljs-literal">true</span>

IF isAccessibility <span class="hljs-keyword">is</span> <span class="hljs-literal">true</span>, <span class="hljs-keyword">set</span> the <span class="hljs-keyword">value</span> to “green” otherwise <span class="hljs-keyword">return</span> “black”

IF isDarkMode <span class="hljs-keyword">is</span> <span class="hljs-literal">false</span> it will skip the inner checks, and <span class="hljs-keyword">return</span> “white”;
</code></pre>
<p>I’d avoid using nested ternary operators where you can, as they’re messy and can be difficult to read.</p>
<h2 id="heading-switch-case-statements">Switch Case Statements</h2>
<p>Switch case statements work by testing a variable (<em>switch</em>) against multiple possibilities (<em>case</em>).</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">string</span> userRole = <span class="hljs-string">"Admin"</span>;

<span class="hljs-comment">// check the user role against following criteria.</span>
<span class="hljs-keyword">switch</span> (userRole)
{
    <span class="hljs-keyword">case</span> <span class="hljs-string">"Admin"</span>:
        Console.WriteLine(<span class="hljs-string">"You have full access."</span>);
        <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-string">"Moderator"</span>:
        Console.WriteLine(<span class="hljs-string">"You can moderate content."</span>);
        <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-string">"User"</span>:
        Console.WriteLine(<span class="hljs-string">"You have limited access."</span>);
        <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">default</span>:
        Console.WriteLine(<span class="hljs-string">"Role not recognised."</span>);
        <span class="hljs-keyword">break</span>;
}
</code></pre>
<p><strong>Note:</strong> <em>the value and type of variable being evaluated must match.</em> For example, you can’t compare a string with a integer (without casting / parsing first)<em>.</em> If none of the cases match, the <code>default</code> block is executed, similar to the <code>else</code> in an <code>if/else</code> structure, usually used for a fallback case.</p>
<h3 id="heading-why-use-switch-case-over-if-statement">Why Use Switch-Case Over If Statement?</h3>
<p><strong>Readability</strong>: When handling <strong>multiple distinct values</strong> (like roles, commands, or enum types), <code>switch</code> is easier to read and maintain because it organises conditions more cleanly, without multiple <code>if</code> and <code>else if</code> blocks.</p>
<p><strong>Performance</strong>: In some cases, <strong>switch</strong> statements can be more efficient than multiple <code>if / else if</code> blocks, especially when dealing with multiple possible values of a single variable. Some compilers optimise <code>switch</code> statements into lookup tables, improving performance</p>
<p><strong>Scalability</strong>: When managing a long list of distinct options or conditions, a <code>switch</code> case scales better. Adding new cases is simpler and doesn't involve updating long chains of <code>if / else if</code>.</p>
<p><strong>Clean Defaults</strong>: The <code>default</code> case provides a clear way to handle unrecognised or unexpected scenarios, similar to the <code>else</code> block in an <code>if / else if</code> statement, but it feels more naturally integrated with the <code>switch</code> structure. This can be handy for throwing exceptions, logging, and so on.</p>
<h3 id="heading-switch-statements-expression-syntax">Switch Statements - Expression Syntax</h3>
<p>There are times where you need slightly more complex conditions to be evaluated yet still wish to utilise the <code>switch</code> syntax. In this scenario you can utilise the <code>switch expression</code> syntax which was introduced in C# 8.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">string</span> userType = <span class="hljs-string">"VIP"</span>;
<span class="hljs-keyword">decimal</span> purchaseAmount = <span class="hljs-number">500</span>m;

<span class="hljs-keyword">decimal</span> discount = userType <span class="hljs-keyword">switch</span>
{
    <span class="hljs-string">"Regular"</span> <span class="hljs-keyword">when</span> purchaseAmount &lt; <span class="hljs-number">100</span> =&gt; <span class="hljs-number">0.05</span>m,  
    <span class="hljs-string">"Regular"</span> =&gt; <span class="hljs-number">0.10</span>m,  
    <span class="hljs-string">"VIP"</span> <span class="hljs-keyword">when</span> purchaseAmount &lt; <span class="hljs-number">500</span> =&gt; <span class="hljs-number">0.15</span>m,  
    <span class="hljs-string">"VIP"</span> =&gt; <span class="hljs-number">0.20</span>m,  
    <span class="hljs-string">"Employee"</span> =&gt; <span class="hljs-number">0.25</span>m,  
    _ =&gt; <span class="hljs-number">0</span>m 
};

Console.WriteLine(<span class="hljs-string">$"Your discount is: <span class="hljs-subst">{discount * <span class="hljs-number">100</span>}</span>%"</span>);
</code></pre>
<p>This syntax is great when needing to assign a value to the outcome of the case.</p>
<ul>
<li><p><code>userType switch { ... }</code>: a <strong>switch expression</strong> that returns a value (in this case, the discount) based on the <code>userType</code> and optional conditions (like <code>purchaseAmount</code>).</p>
</li>
<li><p>The <code>when</code> clause allows for more complex conditions, such as checking the <code>purchaseAmount</code> in addition to the <code>userType</code> to alter the discount amount.</p>
</li>
</ul>
<p><strong>Default Case / Fallback</strong></p>
<p>The default case in this syntax uses the <code>_</code> character. In our example, if none of the above cases are met, the default case is met and we return <code>0m</code> (no discount).</p>
<h3 id="heading-benefits-of-switch-expressions-over-conventional-switch-statements">Benefits of Switch Expressions Over Conventional <code>switch</code> Statements:</h3>
<p><strong>Concise and Readable:</strong> The switch expression is more <strong>compact</strong> and <strong>readable</strong>, especially when returning values directly based on conditions, without needing to declare variables or use <code>break</code> statements.</p>
<p><strong>Pattern Matching:</strong> It supports <strong>pattern matching</strong> (<code>when</code>), making it easier to add complex conditions to each case, something that would require more verbose <code>if</code> checks in a traditional switch statement.</p>
<p><strong>Returns a Value</strong>: The switch expression is an <strong>expression</strong> that directly returns a value, making it ideal for assignments or inline use, reducing boilerplate code. Below, you’ll find an example using a switch expression as the return value in a method without assigning it to a variable.</p>
<p><strong>Simplifies Complex Logic</strong>: It's excellent for scenarios like this one, where multiple conditions affect the outcome, handling them concisely without deeply nested <code>if / else if</code> blocks.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">decimal</span> <span class="hljs-title">CalculateDiscount</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> userType, <span class="hljs-keyword">decimal</span> purchaseAmount</span>)</span> =&gt;
    userType <span class="hljs-keyword">switch</span>
    {
        <span class="hljs-string">"Regular"</span> <span class="hljs-keyword">when</span> purchaseAmount &lt; <span class="hljs-number">100</span> =&gt; <span class="hljs-number">0.05</span>m,
        <span class="hljs-string">"Regular"</span> =&gt; <span class="hljs-number">0.10</span>m,
        <span class="hljs-string">"VIP"</span> <span class="hljs-keyword">when</span> purchaseAmount &lt; <span class="hljs-number">500</span> =&gt; <span class="hljs-number">0.15</span>m,
        <span class="hljs-string">"VIP"</span> =&gt; <span class="hljs-number">0.20</span>m,
        <span class="hljs-string">"Employee"</span> =&gt; <span class="hljs-number">0.25</span>m,
        _ =&gt; <span class="hljs-number">0</span>m
    };

<span class="hljs-comment">// Usage</span>
<span class="hljs-keyword">decimal</span> discount = CalculateDiscount(<span class="hljs-string">"VIP"</span>, <span class="hljs-number">500</span>m);
Console.WriteLine(<span class="hljs-string">$"Your discount is: <span class="hljs-subst">{discount * <span class="hljs-number">100</span>}</span>%"</span>);
</code></pre>
<h2 id="heading-performance-summary">Performance Summary</h2>
<p>To help you decide, here are is a quick Benchmarking project (using BenchmarkDotNet package, which you can install with <code>dotnet add package BenchmarkDotnet</code>):</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> BenchmarkDotNet.Attributes;
<span class="hljs-keyword">using</span> BenchmarkDotNet.Order;
<span class="hljs-keyword">using</span> System.Collections.Generic;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">Csharp_Console_Playground</span>
{
    [<span class="hljs-meta">MemoryDiagnoser</span>]
    [<span class="hljs-meta">Orderer(SummaryOrderPolicy.FastestToSlowest)</span>]
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">BenchMarkRunner</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">readonly</span> Dictionary&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">string</span>&gt; RankingMessages = <span class="hljs-keyword">new</span>()
        {
            { <span class="hljs-number">0</span>, <span class="hljs-string">"Do not stay in this hotel"</span> },
            { <span class="hljs-number">1</span>, <span class="hljs-string">"It's cheap and cheerful"</span> },
            { <span class="hljs-number">2</span>, <span class="hljs-string">"It's clean and tidy"</span> },
            { <span class="hljs-number">3</span>, <span class="hljs-string">"In the middle hotel, good service and great amenities"</span> },
            { <span class="hljs-number">4</span>, <span class="hljs-string">"It's a nice hotel, but the price is not too high"</span> },
            { <span class="hljs-number">5</span>, <span class="hljs-string">"It's the best hotel in the area"</span> }
        };

        <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> ranking = <span class="hljs-number">5</span>;

        [<span class="hljs-meta">Benchmark</span>]
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-title">BenchMarkIfElse</span>(<span class="hljs-params"></span>)</span>
        {
            <span class="hljs-keyword">var</span> winningMessage = <span class="hljs-string">""</span>;

            <span class="hljs-keyword">if</span> (ranking == <span class="hljs-number">0</span>)
            {
                winningMessage = <span class="hljs-string">"Do not stay in this hotel"</span>;
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (ranking == <span class="hljs-number">1</span>)
            {
                winningMessage = <span class="hljs-string">"It's cheap and cheerful"</span>;
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (ranking == <span class="hljs-number">2</span>)
            {
                winningMessage = <span class="hljs-string">"It's clean and tidy"</span>;
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (ranking == <span class="hljs-number">3</span>)
            {
                winningMessage = <span class="hljs-string">"In the middle hotel, good service and great amenities"</span>;
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (ranking == <span class="hljs-number">4</span>)
            {
                winningMessage = <span class="hljs-string">"It's a nice hotel, but the price is not too high"</span>;
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (ranking == <span class="hljs-number">5</span>)
            {
                winningMessage = <span class="hljs-string">"It's the best hotel in the area"</span>;
            }

            <span class="hljs-keyword">return</span> winningMessage;
        }

        [<span class="hljs-meta">Benchmark</span>]
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-title">BenchMarkSwitchCaseExpression</span>(<span class="hljs-params"></span>)</span>
        {
            <span class="hljs-keyword">var</span> winningMessage = ranking <span class="hljs-keyword">switch</span>
            {
                <span class="hljs-number">0</span> =&gt; <span class="hljs-string">"Do not stay in this hotel"</span>,
                <span class="hljs-number">1</span> =&gt; <span class="hljs-string">"It's cheap and cheerful"</span>,
                <span class="hljs-number">2</span> =&gt; <span class="hljs-string">"It's clean and tidy"</span>,
                <span class="hljs-number">3</span> =&gt; <span class="hljs-string">"In the middle hotel, good service and great amenities"</span>,
                <span class="hljs-number">4</span> =&gt; <span class="hljs-string">"It's a nice hotel, but the price is not too high"</span>,
                <span class="hljs-number">5</span> =&gt; <span class="hljs-string">"It's the best hotel in the area"</span>,
                _ =&gt; <span class="hljs-string">""</span>
            };

            <span class="hljs-keyword">return</span> winningMessage;
        }

        [<span class="hljs-meta">Benchmark</span>]
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-title">BenchMarkSwitchCase</span>(<span class="hljs-params"></span>)</span>
        {
            <span class="hljs-keyword">var</span> winningMessage = <span class="hljs-string">""</span>;
            <span class="hljs-keyword">switch</span> (ranking)
            {
                <span class="hljs-keyword">case</span> <span class="hljs-number">0</span>:
                    winningMessage = <span class="hljs-string">"Do not stay in this hotel"</span>;
                    <span class="hljs-keyword">break</span>;
                <span class="hljs-keyword">case</span> <span class="hljs-number">1</span>:
                    winningMessage = <span class="hljs-string">"It's cheap and cheerful"</span>;
                    <span class="hljs-keyword">break</span>;
                <span class="hljs-keyword">case</span> <span class="hljs-number">2</span>:
                    winningMessage = <span class="hljs-string">"It's clean and tidy"</span>;
                    <span class="hljs-keyword">break</span>;
                <span class="hljs-keyword">case</span> <span class="hljs-number">3</span>:
                    winningMessage = <span class="hljs-string">"In the middle hotel, good service and great amenities"</span>;
                    <span class="hljs-keyword">break</span>;
                <span class="hljs-keyword">case</span> <span class="hljs-number">4</span>:
                    winningMessage = <span class="hljs-string">"It's a nice hotel, but the price is not too high"</span>;
                    <span class="hljs-keyword">break</span>;
                <span class="hljs-keyword">case</span> <span class="hljs-number">5</span>:
                    winningMessage = <span class="hljs-string">"It's the best hotel in the area"</span>;
                    <span class="hljs-keyword">break</span>;
                <span class="hljs-keyword">default</span>:
                    winningMessage = <span class="hljs-string">"Invalid ranking"</span>;
                    <span class="hljs-keyword">break</span>;
            }

            <span class="hljs-keyword">return</span> winningMessage;
        }

        [<span class="hljs-meta">Benchmark</span>]
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span>? BenchMarkDictionary()
        {
            <span class="hljs-keyword">return</span> RankingMessages.TryGetValue(ranking, <span class="hljs-keyword">out</span> <span class="hljs-keyword">var</span> rankingMessage) ? rankingMessage : <span class="hljs-literal">null</span>;
        }
    }
}
</code></pre>
<p>You can then run these tests in your <code>Program.cs</code>:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> BenchmarkDotNet.Running;
<span class="hljs-keyword">using</span> Csharp_Console_Playground;

BenchmarkRunner.Run&lt;BenchMarkRunner&gt;();
</code></pre>
<p>Make sure you use your <code>Release</code> build profile in order to run the benchmarks.</p>
<pre><code class="lang-csharp"> dotnet build -c Release
</code></pre>
<p>then execute <code>dotnet</code> on your build location, for example:</p>
<pre><code class="lang-csharp">dotnet bin/Release/net8<span class="hljs-number">.0</span>/FCC-Conditions.dll
</code></pre>
<p><strong>Results:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Method</strong></td><td><strong>Mean (execution time -</strong> <a target="_blank" href="https://en.wikipedia.org/wiki/Nanosecond"><strong>nano seconds</strong></a><strong>)</strong></td></tr>
</thead>
<tbody>
<tr>
<td>BenchMarkSwitchCaseExpression</td><td>0.0000 ns</td></tr>
<tr>
<td>BenchMarkSwitchCase</td><td>0.0003 ns</td></tr>
<tr>
<td>BenchMarkIfElse</td><td>0.3628 ns</td></tr>
<tr>
<td>BenchMarkDictionary</td><td>2.6356 ns</td></tr>
</tbody>
</table>
</div><p><code>switch</code> statements can be faster than dictionaries because the compiler helps optimise them. When the compiler sees a <code>switch</code>, it can create a special lookup table (jump table, or binary search tree) that allows it to find the right case quickly, especially when the cases are close together (like numbers 1, 2, and 3). This means it can check the value almost instantly.</p>
<p>The reason the dictionary is the slowest is due to memory allocation. Within our bench mark test, we needed to create an in-memory variable for the dictionary to be created and stored (time consuming), and then retrieval also needs to be carried out when doing the lookup.</p>
<h4 id="heading-switch-case-expressions"><strong>Switch-Case Expressions:</strong></h4>
<p><strong>Compile-Time Optimisation</strong>: Switch cases are typically optimised at <strong>compile-time</strong> (when you build your code). The compiler analyses the possible case values and generates efficient machine code to handle them. Depending on the cases, this could be via jump tables, or branching / binary search mechanisms.</p>
<p>Since this is all done during <strong>compilation</strong>, the switch statement has no additional overhead at runtime beyond what the compiler has set up.</p>
<h4 id="heading-dictionaries"><strong>Dictionaries</strong>:</h4>
<p><strong>Runtime Construction</strong>: In contrast, a dictionary (for example, <code>Dictionary&lt;TKey, TValue&gt;</code> in C#) is built at <strong>runtime</strong>. The dictionary uses a <strong>hash table</strong> under the hood, where keys are hashed to generate an index that maps to a value. Here’s the key difference:</p>
<ul>
<li><p><strong>Hash Function</strong>: When you add a key to a dictionary, the hash function is applied to the key to determine where the value should be stored.</p>
</li>
<li><p><strong>Collision Handling</strong>: If two keys happen to have the same hash (a "collision"), the dictionary has to resolve it (usually by chaining or probing), which can introduce some additional overhead.</p>
</li>
</ul>
<p>Since dictionaries are built and modified at runtime, they have to perform these operations dynamically, which adds more overhead compared to the static structure of switch cases.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Understanding how to control the flow of your code through conditional logic is a foundational skill in programming, and C# offers various tools to achieve this. Whether you're using <code>if/else</code> statements for simple conditions, the ternary operator for concise assignments, or switch statements for complex logic, each approach has its strengths and ideal use cases.</p>
<p>Switch statements provide clarity and performance optimisations, especially when handling multiple conditions. On the other hand, dictionaries offer flexibility but come with a runtime overhead. Knowing when to use each method allows you to write more efficient, readable, and maintainable code tailored to specific scenarios.</p>
<p>As always if you want to chat about any of my articles you can follow me on <a target="_blank" href="https://x.com/grantdotdev">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Reusable Modal Component in React ]]>
                </title>
                <description>
                    <![CDATA[ When using React, we strive to create reusable components as much as we can to limit the number of components and repetition. This keeps your code “DRY”. DRY is a concept you may have come across—it means “Don’t Repeat Yourself”. DRY is a coding prin... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-react-reusable-modal-component/</link>
                <guid isPermaLink="false">66f2bbda52e6cf7c8b14a457</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Tue, 24 Sep 2024 13:17:14 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1727021808508/312a7af2-5049-4093-9f58-5ef277986598.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When using React, we strive to create reusable components as much as we can to limit the number of components and repetition. This keeps your code “DRY”.</p>
<p>DRY is a concept you may have come across—it means “Don’t Repeat Yourself”. DRY is a coding principle that encourages you to minimize code duplication by using abstractions like functions or modules.</p>
<p>It's important because it reduces redundancy, makes code easier to maintain, improves readability, and decreases the risk of errors during updates.</p>
<h2 id="heading-what-will-this-article-cover">What Will This Article Cover?</h2>
<p>In this article, you’ll learn:</p>
<ul>
<li><p>How to build a modal using React and CSS.</p>
</li>
<li><p>How to ensure that the modal can be reused in multiple scenarios, content and styling.</p>
</li>
<li><p>How to integrate state and callback functions into the modal.</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-will-this-article-cover">What Will This Article Cover?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-core-modal-component">The Core Modal Component</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-props-interface">Props Interface</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-markup">The Markup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-react-useeffect">React useEffect</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-do-we-use-useeffect">When Do We Use useEffect?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-the-reusable-modal">How to Use the Reusable Modal</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-additional-improvements">Additional Improvements</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-the-core-modal-component">The Core Modal Component</h2>
<p>In this section, we'll use React to build a component library. There are multiple patterns that you can follow to do this, but one of my favorite is the atomic design pattern.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> React, {useEffect} <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./Modal.css'</span>

<span class="hljs-keyword">interface</span> Props {
    open: <span class="hljs-built_in">boolean</span>;
    cancelFn?: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
    primaryFn?: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
    closeIcon?: <span class="hljs-built_in">string</span>;
    content?: React.ReactNode;
    titleContent?: React.ReactNode;
    className?: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Modal: React.FC&lt;Props&gt; = <span class="hljs-function">(<span class="hljs-params">props</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> {open, cancelFn, primaryFn, closeIcon, titleContent, content} = props;

    <span class="hljs-comment">// simple useEffect to capture ESC key to close the modal </span>
    useEffect(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">const</span> handleKeyDown = <span class="hljs-function">(<span class="hljs-params">e: KeyboardEvent</span>) =&gt;</span> {
            <span class="hljs-keyword">if</span> (e.key === <span class="hljs-string">'Escape'</span> &amp;&amp; open) {
                <span class="hljs-keyword">if</span> (cancelFn) {
                    cancelFn();
                }
            }
        };

        <span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">'keydown'</span>, handleKeyDown);
        <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">document</span>.removeEventListener(<span class="hljs-string">'keydown'</span>, handleKeyDown);
    }, [open, cancelFn]);


    <span class="hljs-keyword">if</span> (!open) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;

    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">"modalBackground"</span>&gt;
            &lt;div className=<span class="hljs-string">"modalContainer"</span>&gt;
                {titleContent &amp;&amp; (&lt;div className=<span class="hljs-string">"title"</span>&gt;
                        {titleContent}
                        &lt;div className=<span class="hljs-string">"titleCloseBtn"</span>&gt;
                            &lt;button onClick={cancelFn}&gt;{closeIcon ?? <span class="hljs-string">'X'</span>}&lt;/button&gt;
                        &lt;/div&gt;
                    &lt;/div&gt;
                )}

                &lt;div className=<span class="hljs-string">"body"</span>&gt;
                    {content}
                &lt;/div&gt;

                &lt;div className=<span class="hljs-string">"footer"</span>&gt;
                    {secondaryFn &amp;&amp; (
                        &lt;button onClick={secondaryFn} id=<span class="hljs-string">"cancelBtn"</span>&gt;
                            Cancel
                        &lt;/button&gt;
                    )}
                    {primaryFn &amp;&amp; (
                        &lt;button onClick={primaryFn}&gt;Continue&lt;/button&gt;
                    )}
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

    );
};
</code></pre>
<pre><code class="lang-scss">
<span class="hljs-selector-class">.modalBackground</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100vw</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
    <span class="hljs-attribute">background-color</span>: rgb(<span class="hljs-number">33</span>, <span class="hljs-number">33</span>, <span class="hljs-number">33</span>, <span class="hljs-number">0.9</span>);
    <span class="hljs-attribute">position</span>: fixed;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">align-items</span>: center;
}

<span class="hljs-selector-class">.modalContainer</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-direction</span>: column;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">background-color</span>: white;
    <span class="hljs-attribute">box-shadow</span>: rgba(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.35</span>) <span class="hljs-number">0px</span> <span class="hljs-number">5px</span> <span class="hljs-number">15px</span>;

}

<span class="hljs-selector-class">.modalContainer</span> <span class="hljs-selector-class">.title</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-direction</span>: row;
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">justify-content</span>: space-between;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">8px</span>;
    <span class="hljs-attribute">border-top-right-radius</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">border-top-left-radius</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#FFE936</span>;
}

<span class="hljs-selector-class">.titleCloseBtn</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: flex-end;
}

<span class="hljs-selector-class">.titleCloseBtn</span> <span class="hljs-selector-tag">button</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.3rem</span>;
}

<span class="hljs-selector-class">.titleCloseBtn</span> <span class="hljs-selector-tag">button</span> {
    <span class="hljs-attribute">background-color</span>: transparent;
    <span class="hljs-attribute">border</span>: none;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">25px</span>;
    <span class="hljs-attribute">cursor</span>: pointer;
}

<span class="hljs-selector-class">.modalContainer</span> <span class="hljs-selector-class">.body</span> {
    <span class="hljs-attribute">flex</span>: <span class="hljs-number">1</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">16px</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-direction</span>: column;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">text-align</span>: center;
}

<span class="hljs-selector-class">.modalContainer</span> <span class="hljs-selector-class">.footer</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">align-items</span>: center;
}

<span class="hljs-selector-class">.modalContainer</span> <span class="hljs-selector-class">.footer</span> <span class="hljs-selector-tag">button</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">150px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">45px</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">border</span>: none;
    <span class="hljs-attribute">background-color</span>: cornflowerblue;
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">cursor</span>: pointer;
}

<span class="hljs-selector-id">#cancelBtn</span> {
    <span class="hljs-attribute">background-color</span>: crimson;
}
</code></pre>
<p>The code above is the core modal component. Let’s break it down.</p>
<h2 id="heading-props-interface">Props Interface</h2>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> Props {
    open: <span class="hljs-built_in">boolean</span>;
    cancelFn?: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
    primaryFn?: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
    closeIcon?: <span class="hljs-built_in">string</span> | React.ReactNode;
    content?: React.ReactNode;
    titleContent?: React.ReactNode;
}
</code></pre>
<p>In this interface (which we’re passing to the <code>Modal</code> component) we have:</p>
<ul>
<li><p><code>open</code>: A boolean value that signifies whether the modal should be shown or not. A common way of toggling the modal on or off.</p>
</li>
<li><p><code>cancelFn</code>: An optional parameter (denoted by <code>?</code>) that provides a call back function for when the secondary button is being pressed. For example, the <code>cancel</code> functionality to close the modal, or undo an action.</p>
</li>
<li><p><code>primaryFn</code>: An optional parameter that provides a call back function for when the primary button is being pressed. For example, <code>ok</code>, <code>confirm</code>, or <code>submit</code> functionality.</p>
</li>
<li><p><code>closeIcon</code>: An optional parameter that provides an icon to be used as the top right close button for the modal. For example, you could use a circle with an X in it, or another form of a button.</p>
</li>
<li><p><code>content</code>: An optional parameter that provides the inner content for the modal. This could be as simple as a <code>&lt;p/&gt;</code> tag to a fully fledged <code>&lt;form/&gt;</code> element.</p>
</li>
<li><p><code>titleContent</code>: An optional parameter that provides content to be situated within the title section of the modal. This could be anything from text, to a logo image, anything you want.</p>
</li>
</ul>
<h2 id="heading-the-markup">The Markup</h2>
<p>The markup is pretty straightforward, there are <code>divs</code> for each section (title, content, and actions) along with some conditional rendering logic.</p>
<p>That is:</p>
<pre><code class="lang-typescript">{titleContent &amp;&amp; (
    &lt;div className=<span class="hljs-string">"title"</span>&gt;
        {titleContent}
        &lt;div className=<span class="hljs-string">"titleCloseBtn"</span>&gt;
            &lt;button onClick={secondaryFn}&gt;{closeIcon ?? <span class="hljs-string">'X'</span>}&lt;/button&gt;
         &lt;/div&gt;
    &lt;/div&gt;
)}
</code></pre>
<p>We used the short-circuit evaluation syntax to check if the <code>titleContent</code> property is defined by the developer. If it is, the modal’s title is rendered; if not, the title section is omitted.</p>
<p>This approach allows flexible configuration of the modal, letting you easily include or exclude sections like title, content, or actions.</p>
<p>For example, a confirmation modal might only need a title like 'Are you sure?' and action buttons like 'Yes' or 'No', without any additional content.</p>
<h2 id="heading-react-useeffect">React useEffect</h2>
<p>If you’re not familiar with <code>useEffect</code> and plan on using React more, l’d highly recommend reading about it <a target="_blank" href="https://react.dev/reference/react/useEffect">here</a>, as it is one of the backbones of React’s ecosystem.</p>
<p>In essence, <code>useEffect</code> is like a helper that makes sure you do things at the right time in your app.</p>
<h2 id="heading-when-do-we-use-useeffect">When Do We Use <code>useEffect</code>?</h2>
<ol>
<li><p>When you want something to happen right after your app is ready:</p>
<ul>
<li>Example: When the app opens, and you want to fetch some data from the internet (like loading recipes for your dinner).</li>
</ul>
</li>
<li><p>When something a state variable or input prop changes, and you want to do something after that change.</p>
</li>
<li><p>When your app closes or cleans up.</p>
</li>
</ol>
<p>In our React App, we’ve created a <code>useEffect</code> Hook that runs after our modal component has loaded. The <code>useEffect</code> will simply attach a <code>keydown</code> event handler to the <code>document</code> (the page/DOM), which will listen to all keys that are pressed on the screen, and then check if it is the <code>ESC</code> key.</p>
<p>If it is the <code>ESC</code> key, it will call the <code>secondaryFn</code> function passed into the modal. In our case, this is the function that closes the modal. The <code>return</code> statement removes the event handler on unmount (when <code>modalOpen</code> is <code>false</code>).</p>
<h2 id="heading-how-to-use-the-reusable-modal">How to Use the Reusable Modal</h2>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-string">'./App.css'</span>
<span class="hljs-keyword">import</span> {useState} <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> {Modal} <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/molecules/Modal"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{

    <span class="hljs-keyword">const</span> [modalOpen, setModalOpen] = useState(<span class="hljs-literal">false</span>);

    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">"App"</span>&gt;
            &lt;h1&gt;Hey, click on the button to open the modal.&lt;/h1&gt;
            &lt;button className=<span class="hljs-string">"openModalBtn"</span> onClick={<span class="hljs-function">() =&gt;</span> setModalOpen(<span class="hljs-literal">true</span>)}&gt;
                Open
            &lt;/button&gt;

            &lt;Modal 
                open={modalOpen}
                titleContent={&lt;h1&gt; Close &lt;/h1&gt;}
                secondaryFn={<span class="hljs-function">() =&gt;</span> setModalOpen(<span class="hljs-literal">false</span>)}
                content={
                   &lt;&gt;
                     &lt;h2&gt;This is a modal&lt;/h2&gt;
                     &lt;p&gt;You can close it by pressing Escape key, pressing close, or clicking outside the modal.&lt;/p&gt;
                  &lt;/&gt;

               }
           /&gt;
        &lt;/div&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App
</code></pre>
<h3 id="heading-breaking-it-down">Breaking It Down</h3>
<p>In the above code, we have a button component that triggers the modal to be displayed. This is done by updating the <code>useState</code> variable <code>modalOpen</code>. Setting this to <code>true</code> will cause the <code>Modal</code> component to be seen.</p>
<p>Further down the code, we implemented the <code>Modal</code> component and passed in the relevant properties within the modal: a title, body content, and a secondary button (we didn't pass a primary function). This renders the following modal:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726519310027/a88f68a8-7bed-49cf-bbd2-ad4b2f5dea05.png" alt="Image: implemented information modal" class="image--center mx-auto" width="1426" height="760" loading="lazy"></p>
<p>Using the same component, we can also mix it up and build a confirmation modal like so:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726519756233/e9c2bf7f-0ea1-4656-859e-2a9b90a4418d.png" alt="e9c2bf7f-0ea1-4656-859e-2a9b90a4418d" class="image--center mx-auto" width="780" height="660" loading="lazy"></p>
<p>Replacing the previous modal implementation with:</p>
<pre><code class="lang-typescript">&lt;Modal
    open={modalOpen}
    titleContent={&lt;h1&gt; Are you sure? &lt;/h1&gt;}
    cancelFn={<span class="hljs-function">() =&gt;</span> setModalOpen(<span class="hljs-literal">false</span>)}
    primaryFn={<span class="hljs-function">() =&gt;</span> {
        alert(<span class="hljs-string">" You deleted everything everything"</span>);
        setModalOpen(<span class="hljs-literal">false</span>);
    }}
    content={
        &lt;&gt;
            &lt;h4&gt;Do you really want to <span class="hljs-keyword">delete</span> everything?&lt;/h4&gt;
        &lt;/&gt;
    }
/&gt;
</code></pre>
<p>There you have it, you have a <code>Modal</code> component with endless possibilities and configurations, depending on what content you pass to each area of the modal.</p>
<h2 id="heading-additional-improvements">Additional Improvements</h2>
<p>There are some additional improvements</p>
<h3 id="heading-replacing-the-cancel-and-primary-buttons">Replacing the Cancel and Primary Buttons</h3>
<p>Instead of passing the <code>cancelFn</code> and <code>primaryFn</code> properties, you can pass a full component containing the buttons, or any other footer components.  </p>
<p>The updated code should look like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> React, { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./Modal.css'</span>;

<span class="hljs-keyword">interface</span> Props {
    open: <span class="hljs-built_in">boolean</span>;
    escFn: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
    closeIcon?: <span class="hljs-built_in">string</span>;
    content?: React.ReactNode;
    titleContent?: React.ReactNode;
    className?: <span class="hljs-built_in">string</span>;
    actions?: React.ReactNode; <span class="hljs-comment">// This will be used to pass buttons or other actions as children</span>
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Modal: React.FC&lt;Props&gt; = <span class="hljs-function">(<span class="hljs-params">props</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { open, closeIcon, titleContent, content, actions } = props;

    useEffect(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">const</span> handleKeyDown = <span class="hljs-function">(<span class="hljs-params">e: KeyboardEvent</span>) =&gt;</span> {
            <span class="hljs-keyword">if</span> (e.key === <span class="hljs-string">'Escape'</span> &amp;&amp; open) {

            }
        };
        <span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">'keydown'</span>, handleKeyDown);
        <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">document</span>.removeEventListener(<span class="hljs-string">'keydown'</span>, handleKeyDown);
    }, [open]);

    <span class="hljs-keyword">if</span> (!open) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;

    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">"modalBackground"</span>&gt;
            &lt;div className=<span class="hljs-string">"modalContainer"</span>&gt;
                {titleContent &amp;&amp; (
                    &lt;div className=<span class="hljs-string">"title"</span>&gt;
                        {titleContent}
                        &lt;div className=<span class="hljs-string">"titleCloseBtn"</span>&gt;
                            &lt;button&gt;{closeIcon ?? <span class="hljs-string">'X'</span>}&lt;/button&gt;
                        &lt;/div&gt;
                    &lt;/div&gt;
                )}

                &lt;div className=<span class="hljs-string">"body"</span>&gt;
                    {content}
                &lt;/div&gt;

                &lt;div className=<span class="hljs-string">"footer"</span>&gt;
                    {actions &amp;&amp; actions}
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );
};
</code></pre>
<p><strong>Usage:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> handleCancel = <span class="hljs-function">() =&gt;</span> {
    setIsOpen(<span class="hljs-literal">false</span>);
};

<span class="hljs-keyword">const</span> handleContinue = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Continue action'</span>);
};

 &lt;Modal
    open={isOpen}
    titleContent={&lt;h2&gt;Confirm Action&lt;/h2&gt;}
    content={&lt;p&gt;Are you sure you want to proceed?&lt;/p&gt;}
    closeIcon=<span class="hljs-string">"X"</span>
    actions={
        &lt;div className=<span class="hljs-string">"custom-actions"</span>&gt;
           &lt;button onClick={handleCancel}&gt;Cancel&lt;/button&gt;
           &lt;button onClick={handleContinue}&gt;Continue&lt;/button&gt;
        &lt;/div&gt;
    }
/&gt;
</code></pre>
<p>Here, we’re now passing the buttons as a property. You can also design the modal to pass the content as a child component, but this can get messy, as developers may see this at first glance as passing the modal content, rather than just footer elements.</p>
<p>There are pros and cons of doing it this way though:</p>
<p><strong>Pros:</strong></p>
<ul>
<li><p><strong>More flexibility</strong>: Allows you to pass all kinds of elements to the footer section. For example, multiple CTA (Call To Action) buttons, links, or anything you’d like, with custom styling.</p>
</li>
<li><p><strong>Separation of concerns:</strong> The modal is now only responsible for rendering the container (layout, title, content, and so on). The logic of what actions (buttons) to display and their behaviours are handled by the parent component that renders the modal, which makes the modal component cleaner and more reusable.</p>
</li>
<li><p><strong>Improved reusability:</strong> You can pass any JSX as the actions, making it usable for a variety of cases (for example, a modal with form submission buttons or multiple options). This approach is useful when you have modals that need different sets of buttons or interactions dependent on other logic within the parent/modal component. The logic can be handled by a builder function, or within another wrapper component which houses the buttons.</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li><p><strong>More responsibility on the parent component:</strong> You now have to handle the buttons in each instance where you use the <code>Modal</code>. This might result in repetition of the button logic (like <code>handleCancel</code> and <code>handleContinue</code>) in different places if you're not careful.</p>
</li>
<li><p><strong>Slightly more complex usage:</strong> The previous approach allowed you to pass in <code>cancelFn</code> and <code>primaryFn</code> directly (optionally), which might be easier for the majority/simple use cases. Passing actions as children may require more setup.</p>
</li>
<li><p><strong>Inconsistent action layout</strong>: If you're not mindful of your code, you could end up with inconsistent button placement or styles across different instances of the modal. This can be managed by ensuring you always pass consistent markup or styles when passing actions as children, but again, it may become difficult to manage.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Building a reusable modal component in React offers great flexibility and reusability across your application. You can easily adapt the modal to various scenarios, whether it’s a simple confirmation modal or a more complex form submission modal.</p>
<p>However, it’s essential to balance between flexibility and simplicity—too much complexity might overburden the parent components with unnecessary repetition.</p>
<p>Overall, this approach keeps your code DRY, improves maintainability, and empowers you to create scalable UI components. By applying these practices and enhancements, you can build highly adaptable modals that cater to diverse requirements, improving both the developer experience and the final product's quality.</p>
<p>As always, feel free to drop me a follow or reach out on <a target="_blank" href="https://x.com/grantdotdev">Twitter/X</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
