<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://code-corner.dev</id>
    <title>code-corner.dev</title>
    <link href="https://code-corner.dev" />
    <updated>2024-06-18T23:00:00.000Z</updated>
    <category term="dotnetcore" />
    <category term="aspnetcore" />
    <category term="csharp" />
    <category term="patterns" />
    <category term="dotnet" />
    <category term="efcore" />
    <category term="dapper" />
    <category term="hangfire" />
    <category term="linq" />
    <entry>
        <id>https://code-corner.dev/2024/06/19/NET-9-ToList-vs-ToArray/</id>
        <title>.NET 9 — ToList vs ToArray</title>
        <link rel="alternate" href="https://code-corner.dev/2024/06/19/NET-9-ToList-vs-ToArray/"/>
        <content type="html">&lt;p&gt;Last year I made an &lt;a href=&#34;/2023/11/09/NET-%E2%80%94-ToList-vs-ToArray/&#34; title=&#34;.NET — ToList vs ToArray&#34;&gt;article comparing the performance of ToList versus ToArray&lt;/a&gt; when creating short lived collections that won’t be mutated, usually used to prevent multiple enumerations when iterating over a temporary LINQ transformation or to ensure mapping exceptions will be thrown inside the corresponding application layer.&lt;/p&gt;
&lt;p&gt;The tests were performed with .NET Framework 4.8, .NET 7 and .NET 8, which concluded that &lt;code&gt;ToArray&lt;/code&gt; is significantly faster and more memory efficient for almost all collection sizes, with the only exception with very large collections in .NET 8 were &lt;code&gt;ToList&lt;/code&gt; was faster - but still uses more memory).&lt;/p&gt;
&lt;p&gt;Assuming everything goes as planed, &lt;a href=&#34;https://devblogs.microsoft.com/dotnet/our-vision-for-dotnet-9/&#34;&gt;Microsoft should release .NET 9 by the end of 2024&lt;/a&gt;. This is the next major version of their most popular development framework that will bring a lot of new features (C# 13 is one of them) and performance improvements.&lt;/p&gt;
&lt;p&gt;Since we already have .NET 9 preview 5 available, which contains an even more optimized &lt;code&gt;SegmentedArrayBuilder&lt;/code&gt; that is used internally by &lt;code&gt;ToArray&lt;/code&gt;, I think it is a good time to compare the performance of both these methods in .NET 9 while using .NET 8 as the baseline.&lt;/p&gt;
&lt;h1 id=&#34;Performance-Test&#34;&gt;&lt;a href=&#34;#Performance-Test&#34; class=&#34;headerlink&#34; title=&#34;Performance Test&#34;&gt;&lt;/a&gt;Performance Test&lt;/h1&gt;&lt;p&gt;Once again, I’m going to use the well known C# library &lt;code&gt;BenchmarkDotNet&lt;/code&gt; to run the tests and the environment will be the following:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3737/23H2/2023Update/SunValley3)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;AMD Ryzen 7 3700X, 1 CPU, 16 logical and 8 physical cores&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;.NET SDK 9.0.100-preview.5.24307.3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  [Host]               : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  .NET 8.0             : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  .NET 9.0             : .NET 9.0.0 (9.0.24.30607), X64 RyuJIT AVX2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9241.0), X64 RyuJIT VectorSize=256&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The test consists in the creation of a collection that will hold random integers with a size defined by a test parameter. To ensure the collection initialization doesn’t affect performance, it will be created and cached during test setup, but converted to a new &lt;code&gt;IEnumerable&lt;/code&gt; before invoking either &lt;code&gt;ToArray&lt;/code&gt; or &lt;code&gt;ToList&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Keep in mind we want to test the performance of iterating over an &lt;code&gt;IEnumerable&lt;/code&gt; and create either an array or a list so, to prevent .NET internal optimizations (like using a &lt;code&gt;SelectArrayIterator&lt;/code&gt;), the method that converts the cached array to an &lt;code&gt;IEnumerable&lt;/code&gt; will use the &lt;code&gt;yield return&lt;/code&gt; keyword. This is different than the &lt;a href=&#34;/2023/11/09/NET-%E2%80%94-ToList-vs-ToArray/&#34; title=&#34;.NET — ToList vs ToArray&#34;&gt;previous article&lt;/a&gt; were I was using the &lt;code&gt;Select&lt;/code&gt; method which would return an optimized enumerable for arrays and I want to test the worst case scenario.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;SimpleJob(RuntimeMoniker.Net80, baseline: true)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;SimpleJob(RuntimeMoniker.Net90)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;MemoryDiagnoser&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ToListVsToArray&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Params(10, 100, 1000, 10000, 100000)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; Size;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;[] _items;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;GlobalSetup&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Setup&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; random = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Random(&lt;span class=&#34;number&#34;&gt;123&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _items = Enumerable.Range(&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;, Size).Select(_ =&amp;gt; random.Next()).ToArray();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;[] &lt;span class=&#34;title&#34;&gt;ToArray&lt;/span&gt;()&lt;/span&gt; =&amp;gt; CreateItemsEnumerable().ToArray();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; List&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;ToList&lt;/span&gt;()&lt;/span&gt; =&amp;gt; CreateItemsEnumerable().ToList();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; IEnumerable&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;CreateItemsEnumerable&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; item &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; _items)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; item;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Performance-results&#34;&gt;&lt;a href=&#34;#Performance-results&#34; class=&#34;headerlink&#34; title=&#34;Performance results&#34;&gt;&lt;/a&gt;Performance results&lt;/h1&gt;&lt;p&gt;Since this article is a continuation of my &lt;a href=&#34;/2023/11/09/NET-%E2%80%94-ToList-vs-ToArray/&#34; title=&#34;.NET — ToList vs ToArray&#34;&gt;previous one&lt;/a&gt;, were I concluded using &lt;code&gt;ToArray&lt;/code&gt; is faster and more memory efficient than &lt;code&gt;ToList&lt;/code&gt;, let’s compare if that statement still holds true.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Method  | Size   | Mean          | Error         | StdDev        | Gen0     | Gen1     | Gen2     | Allocated |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|-------- |------- |--------------:|--------------:|--------------:|---------:|---------:|---------:|----------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 10     |      70.39 ns |      0.366 ns |      0.342 ns |   0.0134 |        - |        - |     112 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 10     |      72.85 ns |      0.744 ns |      0.696 ns |   0.0315 |        - |        - |     264 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 100    |     322.65 ns |      1.816 ns |      1.610 ns |   0.0563 |        - |        - |     472 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 100    |     368.11 ns |      4.283 ns |      4.006 ns |   0.1469 |        - |        - |    1232 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 1000   |   2,451.62 ns |     19.687 ns |     16.439 ns |   0.4845 |        - |        - |    4072 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 1000   |   2,854.28 ns |     24.286 ns |     22.717 ns |   1.0109 |   0.0153 |        - |    8472 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 10000  |  22,275.27 ns |    163.363 ns |    152.810 ns |   4.7607 |        - |        - |   40072 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 10000  |  26,944.65 ns |    293.685 ns |    260.344 ns |  15.6250 |        - |        - |  131448 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 100000 | 328,160.90 ns |  1,874.673 ns |  1,753.570 ns | 124.5117 | 124.5117 | 124.5117 |  400156 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 100000 | 410,583.73 ns |  2,298.854 ns |  2,037.874 ns | 285.6445 | 285.6445 | 285.6445 | 1049120 B |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Using &lt;code&gt;ToList&lt;/code&gt; as the baseline, we can see the &lt;code&gt;ToArray&lt;/code&gt; method is, on average, 15% faster and uses 60% less memory.&lt;/p&gt;
&lt;p&gt;Keep in mind that &lt;code&gt;ToArray&lt;/code&gt; is a better choice than &lt;code&gt;ToList&lt;/code&gt; even on larger collections, something that wasn’t true in .NET 8, were it was 4% slower despite using less memory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The winner:&lt;/strong&gt; .NET 9.0&lt;/p&gt;
&lt;h1 id=&#34;NET-performance-evolution&#34;&gt;&lt;a href=&#34;#NET-performance-evolution&#34; class=&#34;headerlink&#34; title=&#34;.NET performance evolution&#34;&gt;&lt;/a&gt;.NET performance evolution&lt;/h1&gt;&lt;p&gt;Because we also want to compare the performance of .NET 9 over .NET 8, let’s analyze each method individually on each framework and see if anything has changed.&lt;/p&gt;
&lt;h2 id=&#34;ToArray&#34;&gt;&lt;a href=&#34;#ToArray&#34; class=&#34;headerlink&#34; title=&#34;ToArray&#34;&gt;&lt;/a&gt;ToArray&lt;/h2&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Runtime  | Size   | Mean          | Error         | StdDev        | Ratio | RatioSD | Gen0     | Gen1     | Gen2     | Allocated | Alloc Ratio |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|----------|------- |--------------:|--------------:|--------------:|------:|--------:|---------:|---------:|---------:|----------:|------------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0 | 10     |     107.51 ns |      1.585 ns |      1.238 ns |  1.00 |    0.00 |   0.0315 |        - |        - |     264 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 9.0 | 10     |      70.39 ns |      0.366 ns |      0.342 ns |  0.65 |    0.01 |   0.0134 |        - |        - |     112 B |        0.42 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0 | 100    |     442.33 ns |      3.788 ns |      3.543 ns |  1.00 |    0.00 |   0.1431 |        - |        - |    1200 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 9.0 | 100    |     322.65 ns |      1.816 ns |      1.610 ns |  0.73 |    0.01 |   0.0563 |        - |        - |     472 B |        0.39 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0 | 1000   |   3,186.13 ns |     31.530 ns |     29.493 ns |  1.00 |    0.00 |   1.0185 |        - |        - |    8544 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 9.0 | 1000   |   2,451.62 ns |     19.687 ns |     16.439 ns |  0.77 |    0.00 |   0.4845 |        - |        - |    4072 B |        0.48 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0 | 10000  |  30,659.83 ns |    292.167 ns |    273.293 ns |  1.00 |    0.00 |  12.6343 |        - |        - |  106232 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 9.0 | 10000  |  22,275.27 ns |    163.363 ns |    152.810 ns |  0.73 |    0.00 |   4.7607 |        - |        - |   40072 B |        0.38 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0 | 100000 | 482,397.96 ns |  1,499.949 ns |  1,403.053 ns |  1.00 |    0.00 | 249.5117 | 249.5117 | 249.5117 |  925140 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 9.0 | 100000 | 328,160.90 ns |  1,874.673 ns |  1,753.570 ns |  0.68 |    0.00 | 124.5117 | 124.5117 | 124.5117 |  400156 B |        0.43 |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Using .NET 8 as the baseline, we can see the &lt;code&gt;ToArray&lt;/code&gt; method is, on average, 30% faster and uses 55% less memory on .NET 9. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The winner:&lt;/strong&gt; .NET 9.0&lt;/p&gt;
&lt;h2 id=&#34;ToList&#34;&gt;&lt;a href=&#34;#ToList&#34; class=&#34;headerlink&#34; title=&#34;ToList&#34;&gt;&lt;/a&gt;ToList&lt;/h2&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Runtime  | Size   | Mean          | Error         | StdDev        | Ratio | RatioSD | Gen0     | Gen1     | Gen2     | Allocated | Alloc Ratio |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|----------|------- |--------------:|--------------:|--------------:|------:|--------:|---------:|---------:|---------:|----------:|------------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0 | 10     |      87.44 ns |      1.803 ns |      1.929 ns |  1.00 |    0.00 |   0.0315 |        - |        - |     264 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 9.0 | 10     |      72.85 ns |      0.744 ns |      0.696 ns |  0.84 |    0.02 |   0.0315 |        - |        - |     264 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0 | 100    |     420.90 ns |      3.654 ns |      2.853 ns |  1.00 |    0.00 |   0.1469 |        - |        - |    1232 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 9.0 | 100    |     368.11 ns |      4.283 ns |      4.006 ns |  0.87 |    0.01 |   0.1469 |        - |        - |    1232 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0 | 1000   |   3,448.37 ns |     67.905 ns |     78.199 ns |  1.00 |    0.00 |   1.0109 |   0.0153 |        - |    8472 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 9.0 | 1000   |   2,854.28 ns |     24.286 ns |     22.717 ns |  0.82 |    0.01 |   1.0109 |   0.0153 |        - |    8472 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0 | 10000  |  35,650.35 ns |    707.370 ns |  1,537.764 ns |  1.00 |    0.00 |  15.6250 |        - |        - |  131448 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 9.0 | 10000  |  26,944.65 ns |    293.685 ns |    260.344 ns |  0.77 |    0.05 |  15.6250 |        - |        - |  131448 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0 | 100000 | 462,317.72 ns |  1,686.365 ns |  1,577.427 ns |  1.00 |    0.00 | 285.6445 | 285.6445 | 285.6445 | 1049120 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 9.0 | 100000 | 410,583.73 ns |  2,298.854 ns |  2,037.874 ns |  0.89 |    0.01 | 285.6445 | 285.6445 | 285.6445 | 1049120 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Using .NET 8 as the baseline, we can see the &lt;code&gt;ToList&lt;/code&gt; method is, on average, 15% faster while having exactly the same memory footprint on .NET 9.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The winner:&lt;/strong&gt; .NET 9.0&lt;/p&gt;
&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;In this article we compared the performance of &lt;code&gt;ToArray&lt;/code&gt; versus &lt;code&gt;ToList&lt;/code&gt; on .NET 9 and concluded, once again, if you need to create a temporary collection in memory to prevent multiple enumerations of an &lt;code&gt;IEnumerable&lt;/code&gt;, using &lt;code&gt;ToArray&lt;/code&gt; is more performant in all scenarios independent of collection size, something that wasn’t true in .NET 8.&lt;/p&gt;
&lt;p&gt;This is also a clear statement that Microsoft made a good decision to introduce classes and structures dedicated to performance, like &lt;code&gt;ArrayPool&lt;/code&gt; or &lt;code&gt;ReadOnlySpan&lt;/code&gt;, making it easier to share or reuse resources without constantly (de)allocating memory. This has been specially important for our performance tests since &lt;code&gt;SegmentedArrayBuilder&lt;/code&gt; makes heavy use of these functionalities, now more than ever.&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="csharp" />
        <category term="dotnet" />
        <category term="linq" />
        <updated>2024-06-18T23:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2024/05/25/NET-IAsyncEnumerable-utility-extensions/</id>
        <title>.NET — IAsyncEnumerable utility extensions</title>
        <link rel="alternate" href="https://code-corner.dev/2024/05/25/NET-IAsyncEnumerable-utility-extensions/"/>
        <content type="html">&lt;p&gt;With the introduction of async streams in .NET Core 3, represented by the interface &lt;code&gt;IAsyncEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;, and with a direct support in C# 8 to iterate using &lt;code&gt;await foreach&lt;/code&gt; or easily implement a new asynchronous stream by defining async &lt;code&gt;IAsyncEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; as the method result and using &lt;code&gt;yield return/yield break&lt;/code&gt; just like we did for &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;, Microsoft standardized the way .NET developers implement asynchronous streams.&lt;/p&gt;
&lt;p&gt;Even if we don’t realize, we probably use async streams on a daily basis, from Entity Framework Core to ASP.NET Core, it has become an important part of .NET that is now widely adopted.&lt;br&gt;In this article I’m going to show some the most common scenarios I usually face when working directly with &lt;code&gt;IAsyncEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; and how I usually solve them.&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id=&#34;Utility-extensions-for-async-streams&#34;&gt;&lt;a href=&#34;#Utility-extensions-for-async-streams&#34; class=&#34;headerlink&#34; title=&#34;Utility extensions for async streams&#34;&gt;&lt;/a&gt;Utility extensions for async streams&lt;/h1&gt;&lt;p&gt;When implementing an extension for an async stream there are a few guidelines to keep in mind.&lt;/p&gt;
&lt;p&gt;Firstly, don’t validate function inputs (like null checks) inside the &lt;code&gt;async&lt;/code&gt; method, but create a wrapper method instead that validates and then calls an internal implementation. This will keep the stack trace more clean, easy to analyze and lightweight.&lt;/p&gt;
&lt;p&gt;Secondly, your code will surely need a &lt;code&gt;CancellationToken&lt;/code&gt; but don’t define it in the public method signature even if it has a default value. To make developers life easier, instead of passing a &lt;code&gt;CancellationToken&lt;/code&gt; to every method, Microsoft provides the extension method &lt;code&gt;WithCancellation&lt;/code&gt; that can be used on any &lt;code&gt;IAsyncEnumerable&lt;/code&gt; and if you use the attribute &lt;code&gt;EnumeratorCancellation&lt;/code&gt; in your methods signature, the compiler will automatically use the cancellation token passed to the &lt;code&gt;WithCancellation&lt;/code&gt; method to all your methods. If you define it as a parameter in your public signature it will cause confusion to the developer using it.&lt;/p&gt;
&lt;p&gt;Imagine you were creating a simple &lt;code&gt;Where&lt;/code&gt; extension to filter an async stream, to follow the guidelines it would be implemented as follows:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;IAsyncEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;Where&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    Func&amp;lt;T, &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt;&amp;gt; predicate&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ArgumentNullException.ThrowIfNull(source);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ArgumentNullException.ThrowIfNull(predicate);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Core(source, predicate);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; &lt;span class=&#34;title&#34;&gt;Core&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        Func&amp;lt;T, &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt;&amp;gt; predicate,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        [EnumeratorCancellation] CancellationToken ct = &lt;span class=&#34;literal&#34;&gt;default&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; item &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; source.WithCancellation(ct))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (predicate(item))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; item;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As you can see, the validations are done outside the actual implementation just like the &lt;code&gt;CancellationToken&lt;/code&gt; is only defined internally.&lt;/p&gt;
&lt;p&gt;I recommend giving a good look to the &lt;a href=&#34;https://github.com/dotnet/reactive&#34;&gt;source code&lt;/a&gt; of &lt;code&gt;System.Linq.Async&lt;/code&gt;, the officially supported LINQ extensions for async streams.&lt;/p&gt;
&lt;p&gt;Let’s now drill down to some of my most used utility extensions.&lt;/p&gt;
&lt;h2 id=&#34;Timeout-between-fetches&#34;&gt;&lt;a href=&#34;#Timeout-between-fetches&#34; class=&#34;headerlink&#34; title=&#34;Timeout between fetches&#34;&gt;&lt;/a&gt;Timeout between fetches&lt;/h2&gt;&lt;p&gt;When receiving data from an &lt;code&gt;IAsyncEnumerable&lt;/code&gt; we may not know how many items and how long in total it is going to take, but we may want to enforce a maximum timeout between receiving each item to ensure the application doesn’t wait indefinitely.&lt;/p&gt;
&lt;p&gt;One very common scenario nowadays is the integration with Large Language Models (LLMs) APIs that use Server-Sent Events (SSE) to stream response tokens to be shown to the user in real time. Even with  a loading somewhere, if some token response takes too long the user will probably think the application just stopped working.&lt;/p&gt;
&lt;p&gt;The following code will link a &lt;code&gt;CancellationTokenSource&lt;/code&gt; to the original &lt;code&gt;CancellationToken&lt;/code&gt; and use it’s internal timer to automatically cancel after a given time as passed, throwing a &lt;code&gt;TaskCanceledException&lt;/code&gt; when waiting for the next item.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;IAsyncEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;Timeout&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; millisecondsTimeout&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ArgumentNullException.ThrowIfNull(source);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ArgumentOutOfRangeException.ThrowIfLessThan(millisecondsTimeout, &lt;span class=&#34;number&#34;&gt;-1&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Core(source, millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; &lt;span class=&#34;title&#34;&gt;Core&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; millisecondsTimeout,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        [EnumeratorCancellation] CancellationToken ct = &lt;span class=&#34;literal&#34;&gt;default&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; cts = CancellationTokenSource.CreateLinkedTokenSource(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        cts.CancelAfter(millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; element &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; source.WithCancellation(ct))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;comment&#34;&gt;// disable the timer while the item is being processed&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cts.CancelAfter(&lt;span class=&#34;number&#34;&gt;-1&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; element;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;comment&#34;&gt;// re-enable the timer before fetching the next item&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cts.CancelAfter(millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;I like this approach because it’s simple and, except for an extra allocation of a &lt;code&gt;CancellationTokenSource&lt;/code&gt; and it’s internal &lt;code&gt;Timer&lt;/code&gt; (which will be reused per item), there’s nothing much to be said making it a very efficient implementation. It can be use just like any LINQ method:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; i &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;GetRandomIntegersAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;number&#34;&gt;20&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;                   .&lt;span class=&#34;title&#34;&gt;Timeout&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;number&#34;&gt;500&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;                   .&lt;span class=&#34;title&#34;&gt;WithCancellation&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;ct&lt;/span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Console.WriteLine(&lt;span class=&#34;string&#34;&gt;$&amp;quot;&lt;span class=&#34;subst&#34;&gt;&amp;#123;DateTimeOffset.Now:O&amp;#125;&lt;/span&gt; -&amp;gt; &lt;span class=&#34;subst&#34;&gt;&amp;#123;i&amp;#125;&lt;/span&gt;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you are like me an prefer to receive a &lt;code&gt;TimeoutException&lt;/code&gt;, you can change the implementation by manually iterating over the enumerator instead of using &lt;code&gt;await foreach&lt;/code&gt; and if a &lt;code&gt;TaskCanceledException&lt;/code&gt; is thrown but the received &lt;code&gt;CancellationToken&lt;/code&gt; isn’t cancelled, you can assume it was due to a timeout and throw a &lt;code&gt;TimeoutException&lt;/code&gt; instead.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;IAsyncEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;Timeout&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; millisecondsTimeout&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ArgumentNullException.ThrowIfNull(source);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ArgumentOutOfRangeException.ThrowIfLessThan(millisecondsTimeout, &lt;span class=&#34;number&#34;&gt;-1&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Core(source, millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; &lt;span class=&#34;title&#34;&gt;Core&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; millisecondsTimeout,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        [EnumeratorCancellation] CancellationToken ct = &lt;span class=&#34;literal&#34;&gt;default&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; cts = CancellationTokenSource.CreateLinkedTokenSource(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        cts.CancelAfter(millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; enumerator = source.GetAsyncEnumerator(cts.Token);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; MoveNextCheckTimeoutAsync(enumerator, ct))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cts.CancelAfter(&lt;span class=&#34;number&#34;&gt;-1&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; enumerator.Current;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cts.CancelAfter(millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; ValueTask&amp;lt;&lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;MoveNextCheckTimeoutAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        IAsyncEnumerator&amp;lt;T&amp;gt; enumerator,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; enumerator.MoveNextAsync();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (TaskCanceledException e) &lt;span class=&#34;keyword&#34;&gt;when&lt;/span&gt; (!ct.IsCancellationRequested)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; TimeoutException(&lt;span class=&#34;string&#34;&gt;&amp;quot;The next item took longer than expected to be received&amp;quot;&lt;/span&gt;, e);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Batch-with-maximum-waiting-time&#34;&gt;&lt;a href=&#34;#Batch-with-maximum-waiting-time&#34; class=&#34;headerlink&#34; title=&#34;Batch with maximum waiting time&#34;&gt;&lt;/a&gt;Batch with maximum waiting time&lt;/h2&gt;&lt;p&gt;When receiving data from an &lt;code&gt;IAsyncEnumerable&lt;/code&gt; to be persisted is some database, to reduce and optimize the insertion it may be a good idea to batch it instead of store item by item.&lt;/p&gt;
&lt;p&gt;Imagine you are storing data into some relational table, if you receive 500 items, that’s 500 database accesses which may affect the performance of your overall application. If instead you create batches of 10 items it will mean 50 database accesses with more time in between, certainly reducing the overall database load.&lt;/p&gt;
&lt;p&gt;If we use &lt;a href=&#34;https://morelinq.github.io/&#34;&gt;MoreLINQ&lt;/a&gt; &lt;code&gt;Batch&lt;/code&gt; method as an example, we can easily convert it to an &lt;code&gt;IAsyncEnumerable&lt;/code&gt; implementation.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;IAsyncEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;[]&amp;gt; &lt;span class=&#34;title&#34;&gt;Batch&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; size&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ArgumentNullException.ThrowIfNull(source);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ArgumentOutOfRangeException.ThrowIfNegativeOrZero(size);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Core(source, size);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; IAsyncEnumerable&amp;lt;T[]&amp;gt; Core(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; size,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        [&lt;span class=&#34;meta&#34;&gt;EnumeratorCancellation&lt;/span&gt;] CancellationToken ct = &lt;span class=&#34;literal&#34;&gt;default&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    )&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        T[] batch = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; count = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; item &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; source.WithCancellation(ct))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            batch ??= &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; T[size];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            batch[count++] = item;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (count != size)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;continue&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; batch;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            batch = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (count &amp;gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Array.Resize(&lt;span class=&#34;keyword&#34;&gt;ref&lt;/span&gt; batch, count);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; batch;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Another common scenario when batching items is to support a timeout parameter that will either return a complete batch or, after a given time has passed, it will return whatever items have already been received.&lt;/p&gt;
&lt;p&gt;This can be easily implemented by calculating the a timeout date and check if it has passed every time a item is received. If the given amount of time has passed, you simply resize the collection to the current size, return that batch and calculate again the next timeout date.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;IAsyncEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;[]&amp;gt; &lt;span class=&#34;title&#34;&gt;Batch&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; size,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; millisecondsTimeout&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ArgumentNullException.ThrowIfNull(source);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ArgumentOutOfRangeException.ThrowIfNegativeOrZero(size);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ArgumentOutOfRangeException.ThrowIfNegative(millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Core(source, size, millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; IAsyncEnumerable&amp;lt;T[]&amp;gt; Core(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; size,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; millisecondsTimeout,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        [&lt;span class=&#34;meta&#34;&gt;EnumeratorCancellation&lt;/span&gt;] CancellationToken ct = &lt;span class=&#34;literal&#34;&gt;default&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    )&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        T[] batch = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; count = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; timeoutOn = DateTime.UtcNow.AddMilliseconds(millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; item &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; source.WithCancellation(ct))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            batch ??= &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; T[size];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            batch[count++] = item;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (count != size)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (timeoutOn &amp;gt; DateTime.UtcNow) &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;keyword&#34;&gt;continue&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Array.Resize(&lt;span class=&#34;keyword&#34;&gt;ref&lt;/span&gt; batch, count);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; batch;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            batch = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            timeoutOn = DateTime.UtcNow.AddMilliseconds(millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            count = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (count &amp;gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Array.Resize(&lt;span class=&#34;keyword&#34;&gt;ref&lt;/span&gt; batch, count);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; batch;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;I like this approach because it is very simple to understand and efficient since it only allocates an extra &lt;code&gt;DateTime&lt;/code&gt; over the initial batch method. The only downside is that it only timeouts after an item is received not while waiting. To solve that limitation, the implementation would require a timer and constant allocations of &lt;code&gt;TaskCompletitionSource&lt;/code&gt; which, at least for my use cases, it isn’t worth the complexity and performance overload.&lt;/p&gt;
&lt;p&gt;Again, this method can easily be used just like other LINQ extensions:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; i &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;GetRandomIntegersAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;number&#34;&gt;20&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;                   .&lt;span class=&#34;title&#34;&gt;Batch&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;number&#34;&gt;5&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;1000&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;                   .&lt;span class=&#34;title&#34;&gt;Where&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;e =&amp;gt; e.Length &amp;gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;                   .&lt;span class=&#34;title&#34;&gt;WithCancellation&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;ct&lt;/span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Console.WriteLine(&lt;span class=&#34;string&#34;&gt;$&amp;quot;&lt;span class=&#34;subst&#34;&gt;&amp;#123;DateTimeOffset.Now:O&amp;#125;&lt;/span&gt; -&amp;gt; &lt;span class=&#34;subst&#34;&gt;&amp;#123;i.Length&amp;#125;&lt;/span&gt;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Throttling&#34;&gt;&lt;a href=&#34;#Throttling&#34; class=&#34;headerlink&#34; title=&#34;Throttling&#34;&gt;&lt;/a&gt;Throttling&lt;/h2&gt;&lt;p&gt;One last scenario I usually face is to apply some throttling to prevent overloading the application in case of a high throughput of items received from &lt;code&gt;IAsyncEnumerable&lt;/code&gt;. It usually combines very well with the timeout &lt;code&gt;Batch&lt;/code&gt; method to enforce a predictive cadence of data.&lt;/p&gt;
&lt;p&gt;The implementation is very simple: you calculate the timeout date, fetch the next item, and if not enough time has passed, you just do a delay for the remaining time and repeat the process per each item.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;IAsyncEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;Throttling&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; millisecondsDelay&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ArgumentNullException.ThrowIfNull(source);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ArgumentOutOfRangeException.ThrowIfNegative(millisecondsDelay);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Core(source, millisecondsDelay);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; &lt;span class=&#34;title&#34;&gt;Core&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; millisecondsDelay,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        [EnumeratorCancellation] CancellationToken ct = &lt;span class=&#34;literal&#34;&gt;default&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; timeoutOn = DateTime.UtcNow.AddMilliseconds(millisecondsDelay);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; item &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; source.WithCancellation(ct))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (DateTime.UtcNow &amp;lt; timeoutOn) &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; Task.Delay(timeoutOn - DateTime.UtcNow, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; item;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            timeoutOn = DateTime.UtcNow.AddMilliseconds(millisecondsDelay);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Once again, use it just like other LINQ extensions:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; i &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;GetRandomIntegersAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;number&#34;&gt;20&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;                   .&lt;span class=&#34;title&#34;&gt;Throttling&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;number&#34;&gt;5000&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;                   .&lt;span class=&#34;title&#34;&gt;WithCancellation&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;ct&lt;/span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Console.WriteLine(&lt;span class=&#34;string&#34;&gt;$&amp;quot;&lt;span class=&#34;subst&#34;&gt;&amp;#123;DateTimeOffset.Now:O&amp;#125;&lt;/span&gt; -&amp;gt; &lt;span class=&#34;subst&#34;&gt;&amp;#123;i&amp;#125;&lt;/span&gt;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;In this article I’ve shown some of the most common utility methods I use when working with &lt;code&gt;IAsyncEnumerable&lt;/code&gt;, allowing to either implement timeouts while waiting for the next item, batching with a maximum wait time or enforcing throttling to prevent application overload.&lt;/p&gt;
&lt;p&gt;Feel free to change them in ways that make sense to you, like creating overloads that receive a &lt;code&gt;TimeSpan&lt;/code&gt;, or use them as an example for creating your own utility methods.&lt;/p&gt;
&lt;p&gt;Here’s the full code sample so you can put it in your own projects.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;97&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;98&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;99&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;100&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;101&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;102&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;103&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;104&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;105&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;106&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;107&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;108&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;109&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;110&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;111&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;112&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;113&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;114&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;115&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;116&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;117&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;118&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;119&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;120&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;121&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;122&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;123&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;124&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;125&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;126&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;127&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;128&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;129&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;130&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;131&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;132&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;133&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;134&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;135&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;136&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;137&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;138&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;139&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;140&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;141&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;142&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;143&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;144&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;145&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;146&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;147&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;148&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;149&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;150&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;151&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;152&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;153&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;154&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;155&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;156&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;157&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;158&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;159&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;160&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;161&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;162&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;163&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;164&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;165&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;166&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;167&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;168&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;namespace&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;System.Collections.Generic&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;AsyncEnumerableExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;IAsyncEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;Timeout&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; millisecondsTimeout&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentNullException.ThrowIfNull(source);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentOutOfRangeException.ThrowIfLessThan(millisecondsTimeout, &lt;span class=&#34;number&#34;&gt;-1&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Core(source, millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; &lt;span class=&#34;title&#34;&gt;Core&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;            IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;            &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; millisecondsTimeout,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;            [EnumeratorCancellation] CancellationToken ct = &lt;span class=&#34;literal&#34;&gt;default&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; cts = CancellationTokenSource.CreateLinkedTokenSource(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cts.CancelAfter(millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; enumerator = source.GetAsyncEnumerator(cts.Token);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; MoveNextCheckTimeoutAsync(enumerator, ct))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                cts.CancelAfter(&lt;span class=&#34;number&#34;&gt;-1&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; enumerator.Current;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                cts.CancelAfter(millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ValueTask&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;bool&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;MoveNextCheckTimeoutAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;            IAsyncEnumerator&amp;lt;T&amp;gt; enumerator,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;            CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; enumerator.MoveNextAsync();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (TaskCanceledException e) &lt;span class=&#34;keyword&#34;&gt;when&lt;/span&gt; (!ct.IsCancellationRequested)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; TimeoutException(&lt;span class=&#34;string&#34;&gt;&amp;quot;The next item took longer than expected to be received&amp;quot;&lt;/span&gt;, e);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;IAsyncEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;[]&amp;gt; &lt;span class=&#34;title&#34;&gt;Batch&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; size&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentNullException.ThrowIfNull(source);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentOutOfRangeException.ThrowIfNegativeOrZero(size);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Core(source, size);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; IAsyncEnumerable&amp;lt;T[]&amp;gt; Core(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; size,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            [&lt;span class=&#34;meta&#34;&gt;EnumeratorCancellation&lt;/span&gt;] CancellationToken ct = &lt;span class=&#34;literal&#34;&gt;default&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        )&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            T[] batch = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; count = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; item &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; source.WithCancellation(ct))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                batch ??= &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; T[size];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                batch[count++] = item;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (count != size)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;keyword&#34;&gt;continue&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; batch;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                batch = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (count &amp;gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Array.Resize(&lt;span class=&#34;keyword&#34;&gt;ref&lt;/span&gt; batch, count);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; batch;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;IAsyncEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;[]&amp;gt; &lt;span class=&#34;title&#34;&gt;Batch&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; size,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; millisecondsTimeout&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentNullException.ThrowIfNull(source);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentOutOfRangeException.ThrowIfNegativeOrZero(size);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentOutOfRangeException.ThrowIfNegative(millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Core(source, size, millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; IAsyncEnumerable&amp;lt;T[]&amp;gt; Core(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; size,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; millisecondsTimeout,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            [&lt;span class=&#34;meta&#34;&gt;EnumeratorCancellation&lt;/span&gt;] CancellationToken ct = &lt;span class=&#34;literal&#34;&gt;default&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        )&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            T[] batch = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; count = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; timeoutOn = DateTime.UtcNow.AddMilliseconds(millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; item &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; source.WithCancellation(ct))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                batch ??= &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; T[size];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                batch[count++] = item;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (count != size)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (timeoutOn &amp;gt; DateTime.UtcNow) &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        &lt;span class=&#34;keyword&#34;&gt;continue&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    Array.Resize(&lt;span class=&#34;keyword&#34;&gt;ref&lt;/span&gt; batch, count);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; batch;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                batch = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                timeoutOn = DateTime.UtcNow.AddMilliseconds(millisecondsTimeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                count = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (count &amp;gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Array.Resize(&lt;span class=&#34;keyword&#34;&gt;ref&lt;/span&gt; batch, count);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; batch;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;IAsyncEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;Throttling&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; millisecondsDelay&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentNullException.ThrowIfNull(source);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentOutOfRangeException.ThrowIfNegative(millisecondsDelay);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Core(source, millisecondsDelay);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; IAsyncEnumerable&amp;lt;T&amp;gt; &lt;span class=&#34;title&#34;&gt;Core&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;            IAsyncEnumerable&amp;lt;T&amp;gt; source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;            &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; millisecondsDelay,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;            [EnumeratorCancellation] CancellationToken ct = &lt;span class=&#34;literal&#34;&gt;default&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; timeoutOn = DateTime.UtcNow.AddMilliseconds(millisecondsDelay);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; item &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; source.WithCancellation(ct))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (DateTime.UtcNow &amp;lt; timeoutOn) &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; Task.Delay(timeoutOn - DateTime.UtcNow, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; item;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                timeoutOn = DateTime.UtcNow.AddMilliseconds(millisecondsDelay);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;</content>
        <category term="dotnetcore" />
        <category term="dotnet" />
        <updated>2024-05-24T23:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2024/04/14/NET-9-%E2%80%94-Exception-handling-performance/</id>
        <title>.NET 9 — Exception handling performance</title>
        <link rel="alternate" href="https://code-corner.dev/2024/04/14/NET-9-%E2%80%94-Exception-handling-performance/"/>
        <content type="html">&lt;p&gt;Assuming everything goes as planed, &lt;a href=&#34;https://devblogs.microsoft.com/dotnet/our-vision-for-dotnet-9/&#34;&gt;by the end of 2024 Microsoft should release .NET 9&lt;/a&gt;, which is the next major version of their most popular development framework.&lt;/p&gt;
&lt;p&gt;It will bring a lot of new features (C# 13 is one of them) but also a lot of performance improvements, which have been a major focus ever since Microsoft created the first version of .NET Core.&lt;/p&gt;
&lt;p&gt;Even if an application doesn’t rely on exceptions to hard stop process flows, usually very important for high demanding applications, it certainly connects to a lot of external systems, like databases, message buses, caches and even HTTP endpoints, which may cause exceptions at any moment.&lt;/p&gt;
&lt;p&gt;In this article I’m going to test how exception handling performance improved over the years by comparing how faster is .NET 9 over .NET 8.&lt;/p&gt;
&lt;p&gt;I’m going to use the well known C# library &lt;code&gt;BenchmarkDotNet&lt;/code&gt; to run the tests and the environment will be the following:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.3447/23H2/2023Update/SunValley3)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;AMD Ryzen 7 3700X, 1 CPU, 16 logical and 8 physical cores&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;.NET SDK 9.0.100-preview.3.24204.13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  [Host]               : .NET 8.0.4 (8.0.424.16909), X64 RyuJIT AVX2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  .NET 8.0             : .NET 8.0.4 (8.0.424.16909), X64 RyuJIT AVX2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  .NET 9.0             : .NET 9.0.0 (9.0.24.17209), X64 RyuJIT AVX2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9232.0), X64 RyuJIT VectorSize=256&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;hr&gt;
&lt;h1 id=&#34;Performance-test&#34;&gt;&lt;a href=&#34;#Performance-test&#34; class=&#34;headerlink&#34; title=&#34;Performance test&#34;&gt;&lt;/a&gt;Performance test&lt;/h1&gt;&lt;p&gt;To test the performance of exception handling I’m going to define two simple scenarios:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Ignore exception&lt;/strong&gt; — the simplest possible scenario to measure the performance of throwing and catching an exception;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Convert to string&lt;/strong&gt; — the most common scenario when working with logging frameworks which usually use the ToString method to get a text representation containing both the message and stack trace;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Because asynchronous code with &lt;code&gt;async/await&lt;/code&gt; has become very common and it directly affects the complexity of stack trace information, I’m also going to test both scenarios asynchronously.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;SimpleJob(RuntimeMoniker.Net481)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;SimpleJob(RuntimeMoniker.Net80, baseline: true)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;SimpleJob(RuntimeMoniker.Net90)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;MemoryDiagnoser&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ThrowExceptionPerfTest&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Catch_Ignore&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ThrowException();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Catch_ToString&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ThrowException();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (Exception e)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; e.ToString();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ThrowException&lt;/span&gt;()&lt;/span&gt; =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Exception(&lt;span class=&#34;string&#34;&gt;&amp;quot;test exception&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; ValueTask &lt;span class=&#34;title&#34;&gt;Catch_Ignore_Async&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; ThrowExceptionAsync();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; ValueTask&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;Catch_ToString_Async&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; ThrowExceptionAsync();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (Exception e)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; e.ToString();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; ValueTask &lt;span class=&#34;title&#34;&gt;ThrowExceptionAsync&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; Task.Yield();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Exception(&lt;span class=&#34;string&#34;&gt;&amp;quot;test exception&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Performance-results&#34;&gt;&lt;a href=&#34;#Performance-results&#34; class=&#34;headerlink&#34; title=&#34;Performance results&#34;&gt;&lt;/a&gt;Performance results&lt;/h1&gt;&lt;p&gt;I’m going to use .NET 8 as the baseline since it is the most recent and performant version currently available, but I’m also including .NET Framework 4.8.1 so we can also compare how .NET in general have improved over the years.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Method               | Runtime              | Mean      | Error     | StdDev    | Ratio | RatioSD | Gen0   | Gen1   | Allocated | Alloc Ratio |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|--------------------- |--------------------- |----------:|----------:|----------:|------:|--------:|-------:|-------:|----------:|------------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Catch_Ignore         | .NET 8.0             |  6.130 us | 0.0738 us | 0.0654 us |  1.00 |    0.00 | 0.0381 |      - |     344 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Catch_Ignore         | .NET 9.0             |  3.059 us | 0.0219 us | 0.0183 us |  0.50 |    0.01 | 0.0381 |      - |     344 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Catch_Ignore         | .NET Framework 4.8.1 |  7.178 us | 0.0358 us | 0.0299 us |  1.17 |    0.01 | 0.0687 |      - |     433 B |        1.26 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                      |                      |           |           |           |       |         |        |        |           |             |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Catch_ToString       | .NET 8.0             | 11.472 us | 0.0854 us | 0.0798 us |  1.00 |    0.00 | 0.6409 |      - |    5384 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Catch_ToString       | .NET 9.0             |  7.849 us | 0.1512 us | 0.1263 us |  0.68 |    0.01 | 0.6409 |      - |    5384 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Catch_ToString       | .NET Framework 4.8.1 | 14.393 us | 0.1046 us | 0.0874 us |  1.25 |    0.01 | 1.0376 |      - |    6604 B |        1.23 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                      |                      |           |           |           |       |         |        |        |           |             |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Catch_Ignore_Async   | .NET 8.0             | 21.662 us | 0.4302 us | 1.0056 us |  1.00 |    0.00 | 0.1221 |      - |    1248 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Catch_Ignore_Async   | .NET 9.0             | 11.690 us | 0.1647 us | 0.1375 us |  0.54 |    0.03 | 0.1373 |      - |    1243 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Catch_Ignore_Async   | .NET Framework 4.8.1 | 23.139 us | 0.0875 us | 0.0775 us |  1.06 |    0.06 | 0.2441 |      - |    1663 B |        1.33 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                      |                      |           |           |           |       |         |        |        |           |             |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Catch_ToString_Async | .NET 8.0             | 44.680 us | 0.7175 us | 0.5991 us |  1.00 |    0.00 | 1.0986 |      - |   10025 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Catch_ToString_Async | .NET 9.0             | 36.583 us | 0.7125 us | 0.7623 us |  0.82 |    0.02 | 1.0986 |      - |   10105 B |        1.01 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Catch_ToString_Async | .NET Framework 4.8.1 | 45.369 us | 0.7617 us | 1.0924 us |  1.01 |    0.02 | 1.8921 | 0.0610 |   11999 B |        1.20 |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Let’s analyze each result individualy.&lt;/p&gt;
&lt;h2 id=&#34;Catch-and-ignore&#34;&gt;&lt;a href=&#34;#Catch-and-ignore&#34; class=&#34;headerlink&#34; title=&#34;Catch and ignore&#34;&gt;&lt;/a&gt;Catch and ignore&lt;/h2&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Runtime              | Mean      | Error     | StdDev    | Ratio | RatioSD |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|--------------------- |----------:|----------:|----------:|------:|--------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0             |  6.130 us | 0.0738 us | 0.0654 us |  1.00 |    0.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 9.0             |  3.059 us | 0.0219 us | 0.0183 us |  0.50 |    0.01 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8.1 |  7.178 us | 0.0358 us | 0.0299 us |  1.17 |    0.01 |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The simplest scenario shows .NET 9 with a 50% performance improvement over .NET 8, which was already 15% faster that .NET Framework 4.8.1.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Who’s the winner?&lt;/strong&gt; .NET 9&lt;/p&gt;
&lt;h2 id=&#34;Catch-and-convert-to-string&#34;&gt;&lt;a href=&#34;#Catch-and-convert-to-string&#34; class=&#34;headerlink&#34; title=&#34;Catch and convert to string&#34;&gt;&lt;/a&gt;Catch and convert to string&lt;/h2&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Runtime              | Mean      | Error     | StdDev    | Ratio | RatioSD |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|--------------------- |----------:|----------:|----------:|------:|--------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0             | 11.472 us | 0.0854 us | 0.0798 us |  1.00 |    0.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 9.0             |  7.849 us | 0.1512 us | 0.1263 us |  0.68 |    0.01 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8.1 | 14.393 us | 0.1046 us | 0.0874 us |  1.25 |    0.01 |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;In this scenario, using the &lt;code&gt;ToString&lt;/code&gt; method is 32% faster in .NET 9 than .NET 8, and about 45% faster than .NET Framework 4.8.1.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Who’s the winner?&lt;/strong&gt; .NET 9&lt;/p&gt;
&lt;h2 id=&#34;Catch-and-ignore-async&#34;&gt;&lt;a href=&#34;#Catch-and-ignore-async&#34; class=&#34;headerlink&#34; title=&#34;Catch and ignore (async)&#34;&gt;&lt;/a&gt;Catch and ignore (async)&lt;/h2&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Runtime              | Mean      | Error     | StdDev    | Ratio | RatioSD |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|--------------------- |----------:|----------:|----------:|------:|--------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0             | 21.662 us | 0.4302 us | 1.0056 us |  1.00 |    0.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 9.0             | 11.690 us | 0.1647 us | 0.1375 us |  0.54 |    0.03 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8.1 | 23.139 us | 0.0875 us | 0.0775 us |  1.06 |    0.06 |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;In the simplest scenario, but with an asynchronous implementation, .NET 9 is 46% faster than .NET 8 and 50% faster than .NET Framework 4.8.1.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Who’s the winner?&lt;/strong&gt; .NET 9&lt;/p&gt;
&lt;h2 id=&#34;Catch-and-convert-to-string-async&#34;&gt;&lt;a href=&#34;#Catch-and-convert-to-string-async&#34; class=&#34;headerlink&#34; title=&#34;Catch and convert to string (async)&#34;&gt;&lt;/a&gt;Catch and convert to string (async)&lt;/h2&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Runtime              | Mean      | Error     | StdDev    | Ratio | RatioSD |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|--------------------- |----------:|----------:|----------:|------:|--------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0             | 44.680 us | 0.7175 us | 0.5991 us |  1.00 |    0.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 9.0             | 36.583 us | 0.7125 us | 0.7623 us |  0.82 |    0.02 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8.1 | 45.369 us | 0.7617 us | 1.0924 us |  1.01 |    0.02 |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The catch and use the &lt;code&gt;ToString&lt;/code&gt; method asynchronous implementation is 18% faster in .NET 9 than .NET 8 and 20% faster than .NET framework 4.8.1.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Who’s the winner?&lt;/strong&gt; .NET 9&lt;/p&gt;
&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;In this article we compared .NET 9, .NET 8 and .NET Framework 4.8.1 performance when throwing and handling exceptions, for both synchronous and asynchronous code, and concluded the future .NET release will come with significant improvements.&lt;/p&gt;
&lt;p&gt;This will certainly be important for high demanding systems that usually integrate with a lot of external systems, like message queues, databases or HTTP endpoints. Even in less complex scenarios it is good to know a simple change from .NET 8 or older versions to .NET 9 will increase the overall performance.&lt;/p&gt;
&lt;p&gt;Microsoft has been stepping in the right direction ever since they released .NET Core, and let’s hope they keep going.&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="dotnet" />
        <updated>2024-04-13T23:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2024/02/29/NET-Hangfire/</id>
        <title>.NET — Hangfire</title>
        <link rel="alternate" href="https://code-corner.dev/2024/02/29/NET-Hangfire/"/>
        <content type="html">&lt;p&gt;One common scenario when developing applications is the need to perform background processing than can either be run only once (like sending an email) or scheduled to be run multiple times within a given interval (like doing database housekeeping), usually defined by a &lt;a href=&#34;https://en.wikipedia.org/wiki/Cron&#34;&gt;cron&lt;/a&gt; expression.&lt;/p&gt;
&lt;p&gt;When I need to implement such requirements my first choice for the last few years as always been &lt;a href=&#34;https://www.hangfire.io/&#34;&gt;Hangfire&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It integrates seamlessly with ASP.NET Core applications, it has a simple but very powerful dashboard to monitor and manually trigger recurring jobs and is open source and completely free for commercial use.&lt;/p&gt;
&lt;p&gt;In this article I’m going to explain how to configure both Hangfire server and dashboard into an ASP.NET Core application and recurrently send requests to HTTP endpoints, which will be configured based on application settings without requiring a service restart to detect changes.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;Why-use-Hangfire-to-trigger-HTTP-endpoints&#34;&gt;&lt;a href=&#34;#Why-use-Hangfire-to-trigger-HTTP-endpoints&#34; class=&#34;headerlink&#34; title=&#34;Why use Hangfire to trigger HTTP endpoints?&#34;&gt;&lt;/a&gt;Why use Hangfire to trigger HTTP endpoints?&lt;/h2&gt;&lt;p&gt;An extremely common approach I use when implementing schedulers that may run millions of jobs per day is to use Hangfire to trigger HTTP endpoints, offloading work to external APIs instead of in process.&lt;/p&gt;
&lt;img src=&#34;/2024/02/29/NET-Hangfire/01_http_architecture.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;By design, Hangfire is implemented to be easily distributed on different machines by orchestrating the job execution using a persistent data storage shared across all servers.&lt;/p&gt;
&lt;p&gt;The problem comes when some jobs need more processing power than others or millions of executions per day. You’ll have to maintain multiple nodes looking at different queues, code changes must be properly distributed to the relevant nodes, the database will probably become a bottleneck - even if using some message bus or in-memory cache.&lt;/p&gt;
&lt;p&gt;To attenuate this problem I usually prefer to keep Hangfire instances as simple as possible, by recurrently doing simple HTTP requests that can be configured either by settings files or database providers (&lt;a href=&#34;/2023/12/21/Configuration-providers-in-NET/&#34; title=&#34;Configuration providers in .NET&#34;&gt;like exemplified in this article I recently made&lt;/a&gt;). This allows to offload the heavy work to external APIs that can be more easily scaled, tested, distributed and monitored and ensuring you’ll need much fewer Hangfire instances to run millions of jobs per day while using the dashboard to keep track of job executions.&lt;/p&gt;
&lt;h2 id=&#34;Solution-setup&#34;&gt;&lt;a href=&#34;#Solution-setup&#34; class=&#34;headerlink&#34; title=&#34;Solution setup&#34;&gt;&lt;/a&gt;Solution setup&lt;/h2&gt;&lt;p&gt;In this example, &lt;a href=&#34;https://github.com/gravity00/article-hangfire&#34;&gt;which I have available on GitHub&lt;/a&gt;, I’m going to setup an ASP.NET Core application with .NET 8 to host both Hangfire server and dashboard. For simplicity, the job storage will be in-memory but feel free to use any other of the supported ones (like SQL Server).&lt;/p&gt;
&lt;p&gt;Start by creating an empty ASP.NET Core project and register the following NuGet packages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Hangfire.AspNetCore&lt;/code&gt; - depends  on &lt;code&gt;Hangfire.NetCore&lt;/code&gt;, which is used to run the jobs server on &lt;code&gt;Microsoft.Extensions.Hosting&lt;/code&gt; as a hosted service, but also brings a Web dashboard that can be used to manually trigger recurring jobs or check execution status;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Hangfire.InMemory&lt;/code&gt; - in-memory job storage but you can swap for any other of your preference, like &lt;code&gt;Hangfire.SqlServer&lt;/code&gt;;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Open the &lt;code&gt;Program.cs&lt;/code&gt; file, register both the server and dashboard services into the dependency injection container and configure the dashboard to listen on base address:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; builder = WebApplication.CreateBuilder(args);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.AddHangfire(config =&amp;gt; config&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    .UseInMemoryStorage()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.AddHangfireServer();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; app = builder.Build();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;app.MapHangfireDashboard(&lt;span class=&#34;string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;app.Run();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you run the application, the Hangfire dashboard will be shown with a single server running but without recurring jobs.&lt;/p&gt;
&lt;img src=&#34;/2024/02/29/NET-Hangfire/02_setup.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;As you can see, in just a few minutes you have Hangfire up and ready do run. As stated previously, this is one of the reasons I always use Hangfire for scheduling jobs, the simplicity of it!&lt;/p&gt;
&lt;h2 id=&#34;HTTP-job-options&#34;&gt;&lt;a href=&#34;#HTTP-job-options&#34; class=&#34;headerlink&#34; title=&#34;HTTP job options&#34;&gt;&lt;/a&gt;HTTP job options&lt;/h2&gt;&lt;p&gt;Now that we have both the server and dashboard running, let’s start by defining the recurring HTTP options that we’ll read from the application settings and dynamically configure endpoints to be recurrently triggered.&lt;/p&gt;
&lt;p&gt;Let’s start by defining the options for requesting a single endpoint. To keep things simple, I’ll assume the need for an HTTP address, method, headers and a request timeout. It will also need a &lt;a href=&#34;https://en.wikipedia.org/wiki/Cron&#34;&gt;cron&lt;/a&gt; expression, so we can define the  recurrency interval, and a flag to enable it so you can manage per environment which jobs are enabled.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;HttpJobEndpointOptions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Cron &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; Enabled &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Address &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Method &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; IReadOnlyDictionary&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;, &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt; Headers &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; TimeSpan? Timeout &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; IgnoreInvalidStatusCode &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;We could now load a collection of &lt;code&gt;HttpJobEndpointOptions&lt;/code&gt; from the application settings but let’s try to organize the endpoints into categories and also give a name for each. This will be handy later to have a more readable job name when looking at the Hangfire dashboard.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;HttpJobOptions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; TimeSpan DefaultTimeout &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125; = TimeSpan.FromSeconds(&lt;span class=&#34;number&#34;&gt;5&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; IReadOnlyDictionary&amp;lt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;, &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        IReadOnlyDictionary&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;, HttpJobEndpointOptions&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;gt; Endpoints &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Open the &lt;code&gt;appsettings.json&lt;/code&gt; file and configure an &lt;code&gt;IsAlive&lt;/code&gt; job inside a &lt;code&gt;Core&lt;/code&gt; category that’ll run every 5 minutes. This job will be used later for testing, so assume an HTTP request &lt;code&gt;GET /api/is-alive&lt;/code&gt; to itself:&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;HttpJobs&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;DefaultTimeout&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;00:00:05&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;Endpoints&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;span class=&#34;attr&#34;&gt;&amp;quot;Core&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;attr&#34;&gt;&amp;quot;IsAlive&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          &lt;span class=&#34;attr&#34;&gt;&amp;quot;Cron&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;*/5 * * * *&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          &lt;span class=&#34;attr&#34;&gt;&amp;quot;Enabled&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          &lt;span class=&#34;attr&#34;&gt;&amp;quot;Address&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;https://localhost:7076/api/is-alive&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          &lt;span class=&#34;attr&#34;&gt;&amp;quot;Method&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          &lt;span class=&#34;attr&#34;&gt;&amp;quot;Headers&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;attr&#34;&gt;&amp;quot;accept&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;application/json&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          &lt;span class=&#34;attr&#34;&gt;&amp;quot;Timeout&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;null&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          &lt;span class=&#34;attr&#34;&gt;&amp;quot;IgnoreInvalidStatusCode&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;Logging&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;LogLevel&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;span class=&#34;attr&#34;&gt;&amp;quot;Default&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;Information&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;span class=&#34;attr&#34;&gt;&amp;quot;Microsoft.AspNetCore&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;Warning&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;AllowedHosts&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;*&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Open the &lt;code&gt;Program.cs&lt;/code&gt; file and configure the &lt;code&gt;HttpJobOptions&lt;/code&gt; from the &lt;code&gt;HttpJobs&lt;/code&gt; section. Let’s also add the testing endpoint to be triggered later:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; builder = WebApplication.CreateBuilder(args);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.AddHangfire(config =&amp;gt; config&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    .UseInMemoryStorage()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.AddHangfireServer();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.Configure&amp;lt;HttpJobOptions&amp;gt;(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    builder.Configuration.GetSection(&lt;span class=&#34;string&#34;&gt;&amp;quot;HttpJobs&amp;quot;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; app = builder.Build();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;app.MapGet(&lt;span class=&#34;string&#34;&gt;&amp;quot;/api/is-alive&amp;quot;&lt;/span&gt;, () =&amp;gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;I&amp;#x27;m alive!&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;app.MapHangfireDashboard(&lt;span class=&#34;string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;app.Run();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;HTTP-job-runner&#34;&gt;&lt;a href=&#34;#HTTP-job-runner&#34; class=&#34;headerlink&#34; title=&#34;HTTP job runner&#34;&gt;&lt;/a&gt;HTTP job runner&lt;/h2&gt;&lt;p&gt;Now that we have defined the job options, let’s create the class that will be recurrently executed by the Hangfire server.&lt;/p&gt;
&lt;p&gt;The core idea of this class is to be responsible to do a single HTTP request by receiving both the category and endpoint names as a parameter, looking up for the configuration in the &lt;code&gt;HttpJobOptions&lt;/code&gt; instance and doing the expected HTTP request. It will also receive a &lt;code&gt;PerformContext&lt;/code&gt; instance which is a special (but optional) class provided by Hangfire to give context to your methods.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;HttpJobRunner&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    ILogger&amp;lt;HttpJobRunner&amp;gt; logger,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    IOptionsMonitor&amp;lt;HttpJobOptions&amp;gt; options,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    IHttpClientFactory clientFactory&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; HttpJobOptions Options =&amp;gt; options.CurrentValue;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;RunAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; category, &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; name, PerformContext ctx&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; _ = logger.BeginScope(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;string&#34;&gt;&amp;quot;Category:&amp;#123;Category&amp;#125; Name:&amp;#123;Name&amp;#125; JobId:&amp;#123;JobId&amp;#125;&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            category,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ctx.BackgroundJob.Id&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!Options.Endpoints.TryGetValue(category, &lt;span class=&#34;keyword&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; endpoints))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            logger.LogWarning(&lt;span class=&#34;string&#34;&gt;&amp;quot;Configuration for category not found&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!endpoints.TryGetValue(name, &lt;span class=&#34;keyword&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; endpoint))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            logger.LogWarning(&lt;span class=&#34;string&#34;&gt;&amp;quot;Configuration for endpoint not found&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!endpoint.Enabled)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            logger.LogWarning(&lt;span class=&#34;string&#34;&gt;&amp;quot;Endpoint was disabled while still scheduled, nothing will be done&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; client = clientFactory.CreateClient(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;string&#34;&gt;$&amp;quot;&lt;span class=&#34;subst&#34;&gt;&amp;#123;&lt;span class=&#34;keyword&#34;&gt;nameof&lt;/span&gt;(HttpJobRunner)&amp;#125;&lt;/span&gt;.&lt;span class=&#34;subst&#34;&gt;&amp;#123;category&amp;#125;&lt;/span&gt;.&lt;span class=&#34;subst&#34;&gt;&amp;#123;name&amp;#125;&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        client.Timeout = endpoint.Timeout ?? Options.DefaultTimeout;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        logger.LogDebug(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;string&#34;&gt;&amp;quot;Starting HTTP request &amp;#x27;&amp;#123;Method&amp;#125; &amp;#123;Address&amp;#125;&amp;#x27; [Timeout:&amp;#123;Timeout&amp;#125;]&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            endpoint.Method,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            endpoint.Address,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            client.Timeout&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; request = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; HttpRequestMessage(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; HttpMethod(endpoint.Method),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            endpoint.Address&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (endpoint.Headers?.Count &amp;gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; (key, &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; endpoint.Headers)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                request.Headers.Add(key, &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; ct = ctx.CancellationToken.ShutdownToken;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        HttpResponseMessage response;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            response = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; client.SendAsync(request, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (TaskCanceledException e) &lt;span class=&#34;keyword&#34;&gt;when&lt;/span&gt; (!ct.IsCancellationRequested)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; TimeoutException(&lt;span class=&#34;string&#34;&gt;&amp;quot;Request timed out&amp;quot;&lt;/span&gt;, e);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; (response)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            logger.LogInformation(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;string&#34;&gt;&amp;quot;Completed HTTP request &amp;#x27;&amp;#123;Method&amp;#125; &amp;#123;Address&amp;#125;&amp;#x27; with status code &amp;#123;StatusCode&amp;#125;&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                endpoint.Method,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                endpoint.Address,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                response.StatusCode&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!endpoint.IgnoreInvalidStatusCode)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                response.EnsureSuccessStatusCode();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Register into the DI container the HTTP client services (because we are using the &lt;code&gt;IHttpClientFactory&lt;/code&gt;) and also this class as transient.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; builder = WebApplication.CreateBuilder(args);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.AddHangfire(config =&amp;gt; config&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    .UseInMemoryStorage()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.AddHangfireServer();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.Configure&amp;lt;HttpJobOptions&amp;gt;(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    builder.Configuration.GetSection(&lt;span class=&#34;string&#34;&gt;&amp;quot;HttpJobs&amp;quot;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.AddHttpClient();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.AddTransient&amp;lt;HttpJobRunner&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; app = builder.Build();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;app.MapGet(&lt;span class=&#34;string&#34;&gt;&amp;quot;/api/is-alive&amp;quot;&lt;/span&gt;, () =&amp;gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;I&amp;#x27;m alive!&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;app.MapHangfireDashboard(&lt;span class=&#34;string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;app.Run();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;HTTP-hosted-service&#34;&gt;&lt;a href=&#34;#HTTP-hosted-service&#34; class=&#34;headerlink&#34; title=&#34;HTTP hosted service&#34;&gt;&lt;/a&gt;HTTP hosted service&lt;/h2&gt;&lt;p&gt;It is time to let Hangfire know that we have endpoints that must be requested on a recurrent basis. &lt;/p&gt;
&lt;p&gt;To configure the class &lt;code&gt;HttpJobRunner&lt;/code&gt; as a recurring job, we can use either the static method &lt;code&gt;RecurringJob.AddOrUpdate&lt;/code&gt; or the equivalent methods provided by &lt;code&gt;IRecurringJobManager&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A manual registration of the &lt;code&gt;HTTP:Core:IsAlive&lt;/code&gt; job we defined in the application settings would be as follows:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// using static class RecurringJob&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;RecurringJob.AddOrUpdate&amp;lt;HttpJobRunner&amp;gt;(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;string&#34;&gt;&amp;quot;HTTP:Core:IsAlive&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    runner =&amp;gt; runner.RunAsync(&lt;span class=&#34;string&#34;&gt;&amp;quot;Core&amp;quot;&lt;/span&gt;, &lt;span class=&#34;string&#34;&gt;&amp;quot;IsAlive&amp;quot;&lt;/span&gt;, &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;string&#34;&gt;&amp;quot;*/5 * * * *&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; RecurringJobOptions&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        TimeZone = TimeZoneInfo.Utc&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// using IRecurringJobManager instance&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;jobManager.AddOrUpdate&amp;lt;HttpJobRunner&amp;gt;(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;string&#34;&gt;&amp;quot;HTTP:Core:IsAlive&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    runner =&amp;gt; runner.RunAsync(&lt;span class=&#34;string&#34;&gt;&amp;quot;Core&amp;quot;&lt;/span&gt;, &lt;span class=&#34;string&#34;&gt;&amp;quot;IsAlive&amp;quot;&lt;/span&gt;, &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;string&#34;&gt;&amp;quot;*/5 * * * *&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; RecurringJobOptions&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        TimeZone = TimeZoneInfo.Utc&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As you can see, we pass an expression to the &lt;code&gt;AddOrUpdate&lt;/code&gt; method that will be used by Hangfire to build dynamic code to call the &lt;code&gt;RunAsync&lt;/code&gt; method of our &lt;code&gt;HttpJobRunner&lt;/code&gt; class, including the parameters we specify. As stated before, the &lt;code&gt;PerformContext&lt;/code&gt; parameter is special and despite we are passing  null when defining the expression, Hangfire will actually create a context each time the method is run.&lt;/p&gt;
&lt;p&gt;With this simple yet effective approach, Hangfire doesn’t impose any interface or abstract class that must be implemented, making our life much easier.&lt;/p&gt;
&lt;p&gt;This registration could be done at application startup but since we want do detect changes to the application settings without restarting the server, let’s create a hosted service that will be running in the background and either add or remove scheduler jobs. As a note, since Hangfire doesn’t provide a way to get currently registered jobs so we can remove inexistent, we’ll register the hosted service as a singleton and have a collection in-memory to track registered job ids.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;HttpJobHostedService&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    ILogger&amp;lt;HttpJobHostedService&amp;gt; logger,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    IOptionsMonitor&amp;lt;HttpJobOptions&amp;gt; options,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    IRecurringJobManager jobManager&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;) : IHostedService&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; HashSet&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt; _jobIds = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; IDisposable _onOptionsChange;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;StartAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _onOptionsChange = options.OnChange(jobOptions =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Configuration changed, scheduling jobs&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ScheduleJobs(jobOptions);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Initial configuration, scheduling jobs&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ScheduleJobs(options.CurrentValue);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Task.CompletedTask;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;StopAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _onOptionsChange?.Dispose();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Task.CompletedTask;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ScheduleJobs&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;HttpJobOptions options&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; currentJobIds = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; HashSet&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (options.Endpoints &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &amp;#123; Count: &amp;gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt; &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; (category, endpoints) &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; options.Endpoints)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (endpoints &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;not&lt;/span&gt; &amp;#123; Count: &amp;gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt; &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;keyword&#34;&gt;continue&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; (name, endpoint) &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; endpoints)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!endpoint.Enabled)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        &lt;span class=&#34;keyword&#34;&gt;continue&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; jobId = &lt;span class=&#34;string&#34;&gt;$&amp;quot;HTTP:&lt;span class=&#34;subst&#34;&gt;&amp;#123;category&amp;#125;&lt;/span&gt;:&lt;span class=&#34;subst&#34;&gt;&amp;#123;name&amp;#125;&lt;/span&gt;&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    jobManager.AddOrUpdate&amp;lt;HttpJobRunner&amp;gt;(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        jobId,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        runner =&amp;gt; runner.RunAsync(category, name, &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        endpoint.Cron,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; RecurringJobOptions&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                            TimeZone = TimeZoneInfo.Utc&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    currentJobIds.Add(jobId);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _jobIds.RemoveWhere(jobId =&amp;gt; currentJobIds.Contains(jobId));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; jobId &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; _jobIds) &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            jobManager.RemoveIfExists(jobId);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _jobIds.Clear();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; jobId &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; currentJobIds)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _jobIds.Add(jobId);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Register this class into the Dependency Injection container as a hosted service.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; builder = WebApplication.CreateBuilder(args);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.AddHangfire(config =&amp;gt; config&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    .UseInMemoryStorage()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.AddHangfireServer();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.Configure&amp;lt;HttpJobOptions&amp;gt;(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    builder.Configuration.GetSection(&lt;span class=&#34;string&#34;&gt;&amp;quot;HttpJobs&amp;quot;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.AddHttpClient();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.AddTransient&amp;lt;HttpJobRunner&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.AddHostedService&amp;lt;HttpJobHostedService&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; app = builder.Build();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;app.MapGet(&lt;span class=&#34;string&#34;&gt;&amp;quot;/api/is-alive&amp;quot;&lt;/span&gt;, () =&amp;gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;I&amp;#x27;m alive!&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;app.MapHangfireDashboard(&lt;span class=&#34;string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;app.Run();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you now run the application, the Hangfire dashboard will show the &lt;code&gt;HTTP:Core:IsAlive&lt;/code&gt; recurring job to be run every 5 minutes.&lt;/p&gt;
&lt;img src=&#34;/2024/02/29/NET-Hangfire/03_recurring_jobs_initial.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Try to change the application settings while the application is running, like changing the &lt;em&gt;cron&lt;/em&gt; expression, disabling, removing or creating jobs, so you can see the recurring job list to change accordingly.&lt;/p&gt;
&lt;p&gt;If you wait for the jobs to run (or manually trigger) and go to the &lt;code&gt;Jobs&lt;/code&gt; tab, open the &lt;code&gt;Succeeded&lt;/code&gt; status and you’ll see the list of all jobs that have run successfully.&lt;/p&gt;
&lt;img src=&#34;/2024/02/29/NET-Hangfire/04_jobs_initial.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;If you open one of the jobs, you’ll see details about it’s execution, like the parameters that were passed, the execution time and, if it was failed, the exception details.&lt;/p&gt;
&lt;img src=&#34;/2024/02/29/NET-Hangfire/05_jobs_detail_initial.png&#34; class=&#34;&#34;&gt;

&lt;h2 id=&#34;Job-filters&#34;&gt;&lt;a href=&#34;#Job-filters&#34; class=&#34;headerlink&#34; title=&#34;Job filters&#34;&gt;&lt;/a&gt;Job filters&lt;/h2&gt;&lt;p&gt;The code is looking nice and simple and seems to work very well but there’s still three problems that I think should be solved: &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If you have multiple HTTP endpoints you can’t tell just looking at the list which one was called without checking execution details. In this case every job is called &lt;code&gt;HttpJobRunner.RunAsync&lt;/code&gt;, independently the endpoint;&lt;/li&gt;
&lt;li&gt;Since this is a recurring and stateless job so, when it fails, there’s no point in keeping it in the failed state to be retried, we can move it to the deleted stated that would be automatically housekept by Hangfire;&lt;/li&gt;
&lt;li&gt;If for some reason a job for a given endpoint takes more time to run than its &lt;em&gt;cron&lt;/em&gt; interval, multiple executions will overlap and cause requests to happen concurrently, which may not be the expected behavior;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To solve these problems we are going to use Hangfire filters, which are attributes that can be used to annotate methods (or registered globally) to add custom behavior when running a job, working similarly to ASP.NET Core MVC filters.&lt;/p&gt;
&lt;p&gt;We are going to use the &lt;code&gt;JobDisplayName&lt;/code&gt; filter to define the job name in the dashboard, the &lt;code&gt;AutomaticRetry&lt;/code&gt; filter to mark the job as deleted when failed, and create a custom filter than will lock job executions per endpoint until the previous one has completed.&lt;/p&gt;
&lt;p&gt;Starting by the custom filter, we must extend the &lt;code&gt;JobFilterAttribute&lt;/code&gt; and because it’s changing the behavior of a job execution, it must also implement the &lt;code&gt;IServerFilter&lt;/code&gt; interface. We are going to use both the category and endpoint name parameters to create a unique identifier per endpoint execution and use an Hangfire distributed lock feature to ensure a job for the same endpoint only runs after the lock is disposed by a previous execution.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;EnableDistributedMutexAttribute&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;JobFilterAttribute&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;IServerFilter&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Key = &lt;span class=&#34;keyword&#34;&gt;nameof&lt;/span&gt;(EnableDistributedMutexAttribute);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; _nameFormat;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; TimeSpan _timeout;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;EnableDistributedMutexAttribute&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; nameFormat, &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; timeoutInSeconds&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentNullException.ThrowIfNull(nameFormat);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(timeoutInSeconds, &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _nameFormat = nameFormat;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _timeout = TimeSpan.FromSeconds(timeoutInSeconds);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;OnPerforming&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;PerformingContext ctx&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; distributedLockName = &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;.Format(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _nameFormat,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ctx.BackgroundJob.Job.Args.ToArray()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; distributedLock = ctx.Connection.AcquireDistributedLock(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            distributedLockName,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _timeout&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ctx.Items[Key] = distributedLock;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;OnPerformed&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;PerformedContext ctx&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!ctx.Items.TryGetValue(Key, &lt;span class=&#34;keyword&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; distributedLock))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; InvalidOperationException(&lt;span class=&#34;string&#34;&gt;&amp;quot;Can not release a distributed lock: it was not acquired.&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ((IDisposable)distributedLock).Dispose();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Open the &lt;code&gt;HttpJobRunner&lt;/code&gt; class and annotate the &lt;code&gt;RunAsync&lt;/code&gt; method with a &lt;code&gt;JobDisplayName&lt;/code&gt;, &lt;code&gt;EnableDistributedMutex&lt;/code&gt; and &lt;code&gt;AutomaticRetry&lt;/code&gt; attributes.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;HttpJobRunner&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    ILogger&amp;lt;HttpJobRunner&amp;gt; logger,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    IOptionsMonitor&amp;lt;HttpJobOptions&amp;gt; options,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    IHttpClientFactory clientFactory&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; HttpJobOptions Options =&amp;gt; options.CurrentValue;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;JobDisplayName(&lt;span class=&#34;string&#34;&gt;&amp;quot;HTTP:&amp;#123;0&amp;#125;:&amp;#123;1&amp;#125;&amp;quot;&lt;/span&gt;)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;EnableDistributedMutex(&lt;span class=&#34;string&#34;&gt;&amp;quot;HTTP:&amp;#123;0&amp;#125;:&amp;#123;1&amp;#125;&amp;quot;&lt;/span&gt;, 15)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;AutomaticRetry(OnAttemptsExceeded = AttemptsExceededAction.Delete, Attempts = 0)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;RunAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; category, &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; name, PerformContext ctx&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you now run the application and wait or manually trigger the &lt;code&gt;HTTP:Core:IsAlive&lt;/code&gt; job you’ll see the new display name in the dashboard.&lt;/p&gt;
&lt;img src=&#34;/2024/02/29/NET-Hangfire/06_jobs_final.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Feel free to also force some exceptions to see the job in the deleted state instead of failed, or delaying the job execution for more time than the defined &lt;em&gt;cron&lt;/em&gt; interval to see jobs waiting for the previous ones to complete.&lt;/p&gt;
&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;In this article I explained how easy it is to configure the Hangfire server to run recurring jobs in the background of an ASP.NET Core application, and use its dashboard to manage and monitor job executions.&lt;/p&gt;
&lt;p&gt;For simplicity we used the in-memory job storage and created the base architecture for jobs that recurrently trigger HTTP endpoints, everything configured using application settings.&lt;/p&gt;
&lt;p&gt;As a reminder, this example is &lt;a href=&#34;https://github.com/gravity00/article-hangfire&#34;&gt;available on GitHub&lt;/a&gt; so feel free to give it a look.&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="aspnetcore" />
        <category term="csharp" />
        <category term="hangfire" />
        <updated>2024-02-29T00:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2024/02/05/NET-%E2%80%94-LinkedList-vs-ToArray/</id>
        <title>.NET — LinkedList vs ToArray</title>
        <link rel="alternate" href="https://code-corner.dev/2024/02/05/NET-%E2%80%94-LinkedList-vs-ToArray/"/>
        <content type="html">&lt;p&gt;Some weeks ago I created an &lt;a href=&#34;/2023/11/09/NET-%E2%80%94-ToList-vs-ToArray/&#34; title=&#34;.NET — ToList vs ToArray&#34;&gt;article comparing the performance of ToList versus ToArray&lt;/a&gt; when creating short lived collections that won’t be mutated, usually used to prevent multiple enumerations when iterating over a temporary LINQ transformation or to ensure mapping exceptions will be thrown inside the corresponding application layer.&lt;/p&gt;
&lt;p&gt;In that article I concluded &lt;code&gt;ToArray&lt;/code&gt; is faster and more memory efficient than &lt;code&gt;ToList&lt;/code&gt; for almost any collection sizes and in any .NET version — tests were conducted on .NET Framework 4.8, .NET 7 and .NET 8.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But then I began to wonder:&lt;/strong&gt; &lt;em&gt;If I’m creating a temporary collection of unknown size just to force enumeration, wouldn’t &lt;code&gt;LinkedList&lt;/code&gt; be a more efficient collection since I’m only appending items to the end, which is O(1), instead of constantly allocating new arrays like &lt;code&gt;ToArray&lt;/code&gt; does?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I believe that’s a valid point so I decided to do a performance comparison between the two, assuming &lt;code&gt;ToArray&lt;/code&gt; as the baseline and try to give a detailed explanation for the observed results.&lt;/p&gt;
&lt;h1 id=&#34;Performance-test&#34;&gt;&lt;a href=&#34;#Performance-test&#34; class=&#34;headerlink&#34; title=&#34;Performance test&#34;&gt;&lt;/a&gt;Performance test&lt;/h1&gt;&lt;p&gt;The test consists in the creation of a collection that holds random integers, being the size defined by a parameter. To ensure the randomness does not affect the results, the values are cached into an array and, before invoking either &lt;code&gt;ToArray&lt;/code&gt; or creating the &lt;code&gt;LinkedList&lt;/code&gt;, they are converted into a new &lt;code&gt;IEnumerable&lt;/code&gt;, not just a cast that could lead to internal optimizations.&lt;/p&gt;
&lt;p&gt;This time I decided to do the performance comparison only on .NET Framework 4.8 and .NET 8.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;SimpleJob(RuntimeMoniker.Net48)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;SimpleJob(RuntimeMoniker.Net80)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;MemoryDiagnoser&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;LinkedListVsToArray&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Params(10, 100, 1000, 10000, 100000)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; Size;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;[] _items;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;GlobalSetup&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Setup&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; random = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Random(&lt;span class=&#34;number&#34;&gt;123&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _items = Enumerable.Range(&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;, Size).Select(_ =&amp;gt; random.Next()).ToArray();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark(Baseline = true)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;[] &lt;span class=&#34;title&#34;&gt;ToArray&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; items = CreateItemsEnumerable();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; items.ToArray();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; LinkedList&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;ToLinkedList&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; items = CreateItemsEnumerable();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; LinkedList&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt;(items);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; IEnumerable&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;CreateItemsEnumerable&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; item &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; _items)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; item;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Performance-results&#34;&gt;&lt;a href=&#34;#Performance-results&#34; class=&#34;headerlink&#34; title=&#34;Performance results&#34;&gt;&lt;/a&gt;Performance results&lt;/h1&gt;&lt;p&gt;Because we want to decide, for a given application, between &lt;code&gt;ToArray&lt;/code&gt; or &lt;code&gt;LinkedList&lt;/code&gt; based on performance, let’s analyze the results for each framework version.&lt;/p&gt;
&lt;h2 id=&#34;NET-Framework-4-8&#34;&gt;&lt;a href=&#34;#NET-Framework-4-8&#34; class=&#34;headerlink&#34; title=&#34;.NET Framework 4.8&#34;&gt;&lt;/a&gt;.NET Framework 4.8&lt;/h2&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Method       | Size   | Mean           | Error        | StdDev       | Median         | Ratio | RatioSD | Gen0     | Gen1     | Gen2     | Allocated | Alloc Ratio |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|------------- |------- |---------------:|-------------:|-------------:|---------------:|------:|--------:|---------:|---------:|---------:|----------:|------------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray      | 10     |       143.4 ns |      2.82 ns |      2.64 ns |       143.5 ns |  1.00 |    0.00 |   0.0470 |        - |        - |     297 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToLinkedList | 10     |       175.4 ns |      1.57 ns |      1.39 ns |       175.6 ns |  1.23 |    0.02 |   0.0918 |        - |        - |     578 B |        1.95 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray      | 100    |       891.6 ns |     17.68 ns |     27.00 ns |       883.8 ns |  1.00 |    0.00 |   0.2584 |        - |        - |    1629 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToLinkedList | 100    |     1,464.8 ns |     14.96 ns |     13.26 ns |     1,467.8 ns |  1.63 |    0.04 |   0.7801 |   0.0134 |        - |    4910 B |        3.01 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray      | 1000   |     7,867.5 ns |    156.18 ns |    223.99 ns |     7,896.2 ns |  1.00 |    0.00 |   1.9836 |   0.0153 |        - |   12504 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToLinkedList | 1000   |    14,881.8 ns |    283.19 ns |    290.82 ns |    14,892.3 ns |  1.92 |    0.06 |   7.6599 |   1.0834 |        - |   48238 B |        3.86 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray      | 10000  |    78,138.5 ns |  1,547.95 ns |  2,363.88 ns |    78,993.1 ns |  1.00 |    0.00 |  26.9775 |   5.3711 |        - |  171755 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToLinkedList | 10000  |   158,728.7 ns |  2,151.48 ns |  1,907.23 ns |   158,243.9 ns |  2.03 |    0.07 |  76.4160 |  12.4512 |        - |  481511 B |        2.80 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray      | 100000 |   805,445.4 ns |  9,463.82 ns |  8,852.47 ns |   804,730.5 ns |  1.00 |    0.00 | 399.4141 | 399.4141 | 399.4141 | 1452144 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToLinkedList | 100000 | 3,632,195.6 ns | 49,765.42 ns | 46,550.60 ns | 3,633,088.3 ns |  4.51 |    0.08 | 757.8125 | 375.0000 |        - | 4814292 B |        3.32 |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code&gt;ToArray&lt;/code&gt; method is significantly faster and more memory efficient than creating a new &lt;code&gt;LinkedList&lt;/code&gt;. Still, it does allocate more Gen2 memory for larger collections which may be something to consider.&lt;/p&gt;
&lt;h2 id=&#34;NET-8&#34;&gt;&lt;a href=&#34;#NET-8&#34; class=&#34;headerlink&#34; title=&#34;.NET 8&#34;&gt;&lt;/a&gt;.NET 8&lt;/h2&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Method       | Size   | Mean           | Error        | StdDev       | Median         | Ratio | RatioSD | Gen0     | Gen1     | Gen2     | Allocated | Alloc Ratio |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|------------- |------- |---------------:|-------------:|-------------:|---------------:|------:|--------:|---------:|---------:|---------:|----------:|------------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray      | 10     |       108.8 ns |      2.14 ns |      2.99 ns |       109.0 ns |  1.00 |    0.00 |   0.0315 |        - |        - |     264 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToLinkedList | 10     |       125.7 ns |      2.57 ns |      7.25 ns |       125.9 ns |  1.13 |    0.06 |   0.0677 |        - |        - |     568 B |        2.15 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray      | 100    |       433.3 ns |      7.93 ns |      7.03 ns |       434.0 ns |  1.00 |    0.00 |   0.1431 |        - |        - |    1200 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToLinkedList | 100    |       979.2 ns |     19.54 ns |     40.79 ns |       977.9 ns |  2.34 |    0.09 |   0.5836 |   0.0095 |        - |    4888 B |        4.07 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray      | 1000   |     3,100.4 ns |     34.14 ns |     28.51 ns |     3,090.9 ns |  1.00 |    0.00 |   1.0185 |        - |        - |    8544 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToLinkedList | 1000   |     9,135.9 ns |    179.58 ns |    345.98 ns |     9,205.1 ns |  3.03 |    0.08 |   5.7373 |   0.8850 |        - |   48088 B |        5.63 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray      | 10000  |    31,587.6 ns |    604.95 ns |  1,437.72 ns |    31,129.4 ns |  1.00 |    0.00 |  12.6343 |        - |        - |  106232 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToLinkedList | 10000  |   109,454.7 ns |  2,152.65 ns |  4,198.57 ns |   107,653.8 ns |  3.43 |    0.11 |  57.3730 |  32.9590 |        - |  480088 B |        4.52 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray      | 100000 |   531,028.2 ns |  3,515.73 ns |  3,288.62 ns |   531,061.8 ns |  1.00 |    0.00 | 249.0234 | 249.0234 | 249.0234 |  925140 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToLinkedList | 100000 | 2,295,939.2 ns | 44,598.15 ns | 62,520.38 ns | 2,288,468.0 ns |  4.33 |    0.13 | 570.3125 | 535.1563 |        - | 4800090 B |        5.19 |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Same behavior as .NET Framework 4.8, being the &lt;code&gt;ToArray&lt;/code&gt; method much faster and memory efficient while still allocating more Gen2 memory on larger collections.&lt;/p&gt;
&lt;h1 id=&#34;NET-performance-evolution&#34;&gt;&lt;a href=&#34;#NET-performance-evolution&#34; class=&#34;headerlink&#34; title=&#34;.NET performance evolution&#34;&gt;&lt;/a&gt;.NET performance evolution&lt;/h1&gt;&lt;p&gt;Since in my previous article I already covered the &lt;a href=&#34;/2023/11/09/NET-%E2%80%94-ToList-vs-ToArray/&#34; title=&#34;.NET — ToList vs ToArray&#34;&gt;performance evolution of ToArray over the years&lt;/a&gt;, let’s just compare how initializing a &lt;code&gt;LinkedList&lt;/code&gt; from an &lt;code&gt;IEnumerable&lt;/code&gt; have changed for different frameworks.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Runtime            | Size   | Mean           | Error        | StdDev       | Median         | RatioSD | Gen0     | Gen1     | Gen2     | Allocated |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|------------------- |------- |---------------:|-------------:|-------------:|---------------:|--------:|---------:|---------:|---------:|----------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0           | 10     |       125.7 ns |      2.57 ns |      7.25 ns |       125.9 ns |    0.06 |   0.0677 |        - |        - |     568 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8 | 10     |       175.4 ns |      1.57 ns |      1.39 ns |       175.6 ns |    0.02 |   0.0918 |        - |        - |     578 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0           | 100    |       979.2 ns |     19.54 ns |     40.79 ns |       977.9 ns |    0.09 |   0.5836 |   0.0095 |        - |    4888 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8 | 100    |     1,464.8 ns |     14.96 ns |     13.26 ns |     1,467.8 ns |    0.04 |   0.7801 |   0.0134 |        - |    4910 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0           | 1000   |     9,135.9 ns |    179.58 ns |    345.98 ns |     9,205.1 ns |    0.08 |   5.7373 |   0.8850 |        - |   48088 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8 | 1000   |    14,881.8 ns |    283.19 ns |    290.82 ns |    14,892.3 ns |    0.06 |   7.6599 |   1.0834 |        - |   48238 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0           | 10000  |   109,454.7 ns |  2,152.65 ns |  4,198.57 ns |   107,653.8 ns |    0.11 |  57.3730 |  32.9590 |        - |  480088 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8 | 10000  |   158,728.7 ns |  2,151.48 ns |  1,907.23 ns |   158,243.9 ns |    0.07 |  76.4160 |  12.4512 |        - |  481511 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0           | 100000 | 2,295,939.2 ns | 44,598.15 ns | 62,520.38 ns | 2,288,468.0 ns |    0.13 | 570.3125 | 535.1563 |        - | 4800090 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8 | 100000 | 3,632,195.6 ns | 49,765.42 ns | 46,550.60 ns | 3,633,088.3 ns |    0.08 | 757.8125 | 375.0000 |        - | 4814292 B |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;On average, .NET 8 is 51% faster than .NET Framework 4.8, a clear demonstration how performance has improved over the years.&lt;/p&gt;
&lt;h1 id=&#34;Analyzing-the-results&#34;&gt;&lt;a href=&#34;#Analyzing-the-results&#34; class=&#34;headerlink&#34; title=&#34;Analyzing the results&#34;&gt;&lt;/a&gt;Analyzing the results&lt;/h1&gt;&lt;p&gt;Now that we have the results you are probably questioning why does a &lt;code&gt;LinkedList&lt;/code&gt; use more memory and is slower than &lt;code&gt;ToArray&lt;/code&gt;? Since we don’t know the collection size, shouldn’t the constant allocation of new arrays and then a final copy to a trimmed one be slower and take more memory?&lt;/p&gt;
&lt;p&gt;Using more memory could actually be expectable since a &lt;code&gt;LinkedList&lt;/code&gt;, for each item, creates a class that holds a reference for the previous and next node, but why is it slower since adding an item is always a O(1) operation?&lt;/p&gt;
&lt;p&gt;Based on personal experience, most developers I questioned about the &lt;code&gt;ToArray&lt;/code&gt; implementation assume the following code (&lt;a href=&#34;https://stackoverflow.com/a/16323412/1841558&#34;&gt;I actually believe this Stack Overflow response is to blame&lt;/a&gt;):&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; T[] &lt;span class=&#34;title&#34;&gt;ToArray&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;IEnumerable&amp;lt;T&amp;gt; items&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; sizeIncrease = &lt;span class=&#34;number&#34;&gt;8&lt;/span&gt;; &lt;span class=&#34;comment&#34;&gt;// some arbitrary size increase&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; current = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; T[sizeIncrease];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; count = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; item &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; items)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (count == current.Length)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; previous = current;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            current = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; T[previous.Length + sizeIncrease];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            previous.CopyTo(current, &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        current[count++] = item;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (count &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Array.Empty&amp;lt;T&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Array.Resize(&lt;span class=&#34;keyword&#34;&gt;ref&lt;/span&gt; current, count);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; current;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;It assumes the &lt;code&gt;ToArray&lt;/code&gt; method behaves similar to a &lt;code&gt;List&lt;/code&gt;, creating a bigger one every time the current is full and doing a copy, and in the end it trims the excess.&lt;/p&gt;
&lt;p&gt;In reality, the implementation is more similar to this:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; T[] &lt;span class=&#34;title&#34;&gt;ToArray&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;IEnumerable&amp;lt;T&amp;gt; items&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; sizeIncrease = &lt;span class=&#34;number&#34;&gt;8&lt;/span&gt;; &lt;span class=&#34;comment&#34;&gt;// some arbitrary size increase&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; arrayBuffer = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Queue&amp;lt;T[]&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; current = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; T[sizeIncrease];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; count = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; idx = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; item &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; items)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (idx == sizeIncrease)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            arrayBuffer.Enqueue(current);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            current = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; T[sizeIncrease];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            idx = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        current[idx++] = item;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ++count;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (count &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Array.Empty&amp;lt;T&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; currentIdx = idx;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    idx = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; final = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; T[count];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; (arrayBuffer.Count &amp;gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; previous = arrayBuffer.Dequeue();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        previous.CopyTo(final, idx);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        idx += previous.Length;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Array.Copy(current, &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;, final, idx, currentIdx);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; final;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Instead of creating a bigger array every time it’s full and copying all items, it actually creates a buffer that stores the previously allocated arrays and when the iteration finishes it copies everything in sequence to the final array that has the expected size.&lt;/p&gt;
&lt;p&gt;The actual &lt;code&gt;ToArray&lt;/code&gt; method implementation as a lot of optimizations, for example, when an &lt;code&gt;ICollection&lt;/code&gt; is received they can initialize a new array with the collection size and just call &lt;code&gt;CopyTo&lt;/code&gt;. It also doesn’t use a &lt;code&gt;Queue&lt;/code&gt; to keep track of previous initialized arrays but instead a simple version of a &lt;code&gt;LinkedList&lt;/code&gt; concept.&lt;/p&gt;
&lt;p&gt;Give a look to the internal classes &lt;a href=&#34;https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/System/Collections/Generic/EnumerableHelpers.cs&#34;&gt;EnumerableHelpers&lt;/a&gt; and &lt;a href=&#34;https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/System/Collections/Generic/ArrayBuilder.cs&#34;&gt;ArrayBuilder&lt;/a&gt; for more implementation details.&lt;/p&gt;
&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;In this article we compared the performance of initializing a &lt;code&gt;LinkedList&lt;/code&gt; versus using &lt;code&gt;ToArray&lt;/code&gt; and concluded that, on theory, &lt;code&gt;LinkedList&lt;/code&gt; looked like a good fit when creating short lived collections were enumeration must be forced because of its O(1) nature when appending values, but due to internal optimizations of &lt;code&gt;ToArray&lt;/code&gt; and since indexing is just faster while using less memory it significantly compensates having to allocate multiple arrays and copying their content.&lt;/p&gt;
&lt;p&gt;Keep in mind that &lt;code&gt;LinkedList&lt;/code&gt; still has its place, specially when storing collections too big to be stored into a single memory section and it’s nice to see that .NET keeps improving its performance.&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="csharp" />
        <category term="dotnet" />
        <category term="linq" />
        <updated>2024-02-05T00:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2024/01/19/NET-%E2%80%94-TaskCompletionSource-and-CancellationTokenSource/</id>
        <title>.NET — TaskCompletionSource and CancellationTokenSource</title>
        <link rel="alternate" href="https://code-corner.dev/2024/01/19/NET-%E2%80%94-TaskCompletionSource-and-CancellationTokenSource/"/>
        <content type="html">&lt;p&gt;When Microsoft released .NET Framework 4.0 in April 2010, the &lt;a href=&#34;https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-parallel-library-tpl&#34;&gt;Task Parallel Library (TPL)&lt;/a&gt; was introduced to help developers replace the previously used &lt;a href=&#34;https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/asynchronous-programming-model-apm&#34;&gt;Asynchronous Programming Model (APM)&lt;/a&gt; pattern for a &lt;a href=&#34;https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-based-asynchronous-programming&#34;&gt;Task-based asynchronous programming&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before the introduction of Tasks, when implementing asynchronous code, developers had to define two variations of the same method: one to begin the operation execution (convention: &lt;code&gt;BeginOperationName&lt;/code&gt;), that would receive an optional callback to be invoked when completed, and another to wait for the operation to complete (convention: &lt;code&gt;EndOperationName&lt;/code&gt;) and get the result or an exception, usually used inside the callback to prevent the main thread to be blocked.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;As an example, imagine a repository of cars with an asynchronous method for getting one by a given plate number, implemented using the APM pattern:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ICarRepository&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;IAsyncResult &lt;span class=&#34;title&#34;&gt;BeginGetByPlateNumber&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; plateNumber,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        AsyncCallback callback,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; state&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;Car &lt;span class=&#34;title&#34;&gt;EndGetByPlateNumber&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IAsyncResult ar&lt;/span&gt;)&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// non-blocking usage&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;carRepository.BeginGetByPlateNumber(plateNumber, ar =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; car = carRepository.EndGetByPlateNumber(ar);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;, &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// blocking usage&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; ar = carRepository.BeginGetByPlateNumber(plateNumber, &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;, &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; car = carRepository.EndGetByPlateNumber(ar);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As you can see, creating two methods per operation is one of the most obvious and annoying disadvantages of the APM pattern, while the other is the need to implement your own wrapper for &lt;code&gt;IAsyncResult&lt;/code&gt; so you can trigger the &lt;code&gt;WaitHandle&lt;/code&gt; and invoke the callbacks when the operation completes.&lt;/p&gt;
&lt;p&gt;Another disadvantage was the cancellation of running operations. If it was supported — and that’s a big if — there wasn’t a standardized pattern for developers to follow. Some would create another method for canceling (i.e. &lt;code&gt;CancelOperationName&lt;/code&gt;) that receives an &lt;code&gt;IAsyncResult&lt;/code&gt;, others would provide a method directly into the &lt;code&gt;IAsyncResult&lt;/code&gt; and either return their own interface or require a cast, and others would simply ignore this feature due to sheer complexity.&lt;/p&gt;
&lt;p&gt;Because Microsoft knew asynchronous programming was very important for the future of .NET, it decided to solve these problems by introducing the Task Parallel Library, making it easier for developers to add parallelism and concurrency to applications.&lt;/p&gt;
&lt;p&gt;The Task Parallel Library has two central pieces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;CancellationToken&lt;/code&gt; is a structure commonly used in asynchronous methods and enables developers to register a callback that will be invoked if a cancellation is requested. This provides a standardized approach for implementing asynchronous operations that can be canceled mid execution by simply receiving a &lt;code&gt;CancellationToken&lt;/code&gt; as a method parameter.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Task&lt;/code&gt; and &lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt; are classes that merge both &lt;code&gt;IAsyncResult&lt;/code&gt; and &lt;code&gt;AsyncCallback&lt;/code&gt; concepts. Developers can return a Task and the caller would either register a callback with &lt;code&gt;ContinueWith&lt;/code&gt;, for a non-blocking approach, or use the method &lt;code&gt;Wait/Result&lt;/code&gt; and block the main thread until the &lt;code&gt;Task&lt;/code&gt; was completed. This removes the need to have callback parameters and only a single method is needed instead of &lt;code&gt;BeginX/EndX&lt;/code&gt; methods.&lt;br&gt;If we change the previous car repository example from APM pattern to use TPL, it would be much simpler:&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ICarRepository&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;Task&amp;lt;Car&amp;gt; &lt;span class=&#34;title&#34;&gt;GetByPlateNumberAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; plateNumber,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// non-blocking usage&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;carRepository.GetByPlateNumberAsync(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    plateNumber,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    CancellationToken.None&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;).ContinueWith(t =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; car = t.Result;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// blocking usage&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; car = carRepository.GetByPlateNumberAsync(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    plateNumber,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    CancellationToken.None&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;).Result;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now that we have an idea about &lt;code&gt;Task&lt;/code&gt;, &lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;CancellationToken&lt;/code&gt; and some of the reasons why Microsoft created the Task Parallel Library, let’s analyze two other important classes that aren’t commonly used but make all of this possible — &lt;code&gt;TaskCompletionSource&lt;/code&gt; and &lt;code&gt;CancellationTokenSource&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id=&#34;TaskCompletionSource&#34;&gt;&lt;a href=&#34;#TaskCompletionSource&#34; class=&#34;headerlink&#34; title=&#34;TaskCompletionSource&#34;&gt;&lt;/a&gt;TaskCompletionSource&lt;/h1&gt;&lt;p&gt;The class &lt;code&gt;TaskCompletionSource&lt;/code&gt; is used to create a &lt;code&gt;Task&lt;/code&gt; and provides methods to mark it as completed in one from three possible states:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;RanToCompletion&lt;/strong&gt; — the methods &lt;code&gt;SetResult&lt;/code&gt; or &lt;code&gt;TrySetResult&lt;/code&gt; complete the task successfully and, in case of a &lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt;, the result can be retrieved;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Canceled&lt;/strong&gt; — the methods &lt;code&gt;SetCanceled&lt;/code&gt; or &lt;code&gt;TrySetCanceled&lt;/code&gt; mark the task as cancelled mid-execution. Waiting or retrieving the result will throw a &lt;code&gt;TaskCanceledException&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Faulted&lt;/strong&gt; — the methods &lt;code&gt;SetException&lt;/code&gt; or &lt;code&gt;TrySetException&lt;/code&gt; mark the task as faulted and waiting or retrieving the result will throw the exception.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With this class developers can easily implement Task-based asynchronous programming. On later versions of the .NET Framework it was widely used to convert classes implementing the APM pattern and, with an increase adoption of tasks, developers also started to migrate their own libs.&lt;/p&gt;
&lt;p&gt;Let’s imagine the car repository was still implemented using the APM pattern and we wanted to create some extension methods for developers that prefer to use tasks.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ICarRepository&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;IAsyncResult &lt;span class=&#34;title&#34;&gt;BeginGetByPlateNumber&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; plateNumber,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        AsyncCallback callback,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; state&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;Car &lt;span class=&#34;title&#34;&gt;EndGetByPlateNumber&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IAsyncResult ar&lt;/span&gt;)&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CarRepositoryExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; Task&amp;lt;Car&amp;gt; &lt;span class=&#34;title&#34;&gt;GetByPlateNumberAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; ICarRepository repository,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; plateNumber&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; tcs = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; TaskCompletionSource&amp;lt;Car&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// use callback for non-blocking approach&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        repository.BeginGetByPlateNumber(plateNumber, ar =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; car = repository.EndGetByPlateNumber(ar);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;comment&#34;&gt;// no exception thrown, mark the task as completed successfully&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                tcs.SetResult(car);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (Exception e)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;comment&#34;&gt;// exception thrown, mark the task as faulted&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                tcs.SetException(e);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; tcs.Task;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As you can see, we just converted &lt;code&gt;BeginX/EndX&lt;/code&gt; methods to tasks in just a few lines without blocking the caller thread.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;TaskCompletionSource&lt;/code&gt; is also perfect to convert &lt;strong&gt;Event-based Asynchronous Pattern (EAP)&lt;/strong&gt;. Imagine you have a class that represents a message queue and it provides two events — &lt;code&gt;OnMessageReceived&lt;/code&gt; and &lt;code&gt;OnErrorReceived&lt;/code&gt; — and a non-blocking method to send a message.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;IMessageQueue&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;event&lt;/span&gt; Action&amp;lt;&lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt;, OnMessageReceivedEventArgs&amp;gt; OnMessageReceived;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;event&lt;/span&gt; Action&amp;lt;&lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt;, OnErrorReceivedEventArgs&amp;gt; OnErrorReceived;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Send&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;Guid correlationId, JObject content&lt;/span&gt;)&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;OnMessageReceivedEventArgs&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;EventArgs&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Guid CorrelationId &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; JObject Content &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;OnErrorReceivedEventArgs&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;EventArgs&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Guid CorrelationId &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Exception Exception &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Let’s say we wanted to create an extension method that sends a message and, without blocking the current thread, waits for a correlated response or exception to be received.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;MessageQueueExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task&amp;lt;JObject&amp;gt; &lt;span class=&#34;title&#34;&gt;SendAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; IMessageQueue queue,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        Guid correlationId,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        JObject content&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; tcs = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; TaskCompletionSource&amp;lt;JObject&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// register the events before sending the message&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        queue.OnMessageReceived += OnMessageReceived;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        queue.OnErrorReceived += OnErrorReceived;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            queue.Send(correlationId, content);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; tcs.Task;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;finally&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;comment&#34;&gt;// ensure events registration is always cleaned up&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            queue.OnMessageReceived -= OnMessageReceived;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            queue.OnErrorReceived -= OnErrorReceived;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;OnMessageReceived&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; _, OnMessageReceivedEventArgs args&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (args.CorrelationId == correlationId) &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                tcs.TrySetResult(args.Content);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;OnErrorReceived&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; _, OnErrorReceivedEventArgs args&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (args.CorrelationId == correlationId) &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                tcs.TrySetException(args.Exception);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The approach is very similar to the APM pattern. We do a temporary registration into the relevant events, then invoke the action that can trigger those events and change the task state when callbacks are invoked. Just be sure registrations are properly cleaned up or else a memory leak will happen.&lt;/p&gt;
&lt;h1 id=&#34;CancellationTokenSource&#34;&gt;&lt;a href=&#34;#CancellationTokenSource&#34; class=&#34;headerlink&#34; title=&#34;CancellationTokenSource&#34;&gt;&lt;/a&gt;CancellationTokenSource&lt;/h1&gt;&lt;p&gt;The class &lt;code&gt;CancellationTokenSource&lt;/code&gt; is used to create a &lt;code&gt;CancellationToken&lt;/code&gt; that can be manually marked as canceled using the method &lt;code&gt;Cancel&lt;/code&gt; or, to support timeout implementations, the &lt;code&gt;CancelAfter&lt;/code&gt; methods that will schedule a cancellation after the specified time as passed.&lt;/p&gt;
&lt;p&gt;Using the previously defined &lt;code&gt;GetByPlateNumberAsync&lt;/code&gt; extension method, let’s change it first to support cancellation using a &lt;code&gt;CancellationToken&lt;/code&gt;. Please note that some performance improvements could be made when detecting for cancellation, but this implementation is for simplicity:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CarRepositoryExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task&amp;lt;Car&amp;gt; &lt;span class=&#34;title&#34;&gt;GetByPlateNumberAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; ICarRepository repository,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; plateNumber,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; tcs = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; TaskCompletionSource&amp;lt;Car&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; _ = ct.Register(() =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;comment&#34;&gt;// if the cancellation is triggered we try to cancel the task&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            tcs.TrySetCanceled(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// use callback for non-blocking approach&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        repository.BeginGetByPlateNumber(plateNumber, ar =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; car = repository.EndGetByPlateNumber(ar);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;comment&#34;&gt;// because the task may have been cancelled first we&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;comment&#34;&gt;// must try to set a result or an exception will be thrown&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                tcs.TrySetResult(car);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (Exception e)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;comment&#34;&gt;// same, task may have been cancelled first so we try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;comment&#34;&gt;// to set an exception and mark it as faulted&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                tcs.TrySetException(e);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; tcs.Task;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now that &lt;code&gt;GetByPlateNumberAsync&lt;/code&gt; supports cancellation, if we wanted to limit the waiting for a response to a maximum of 5 seconds, a &lt;code&gt;CancellationTokenSource&lt;/code&gt; could be used:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; cts = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CancellationTokenSource();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;cts.CancelAfter(TimeSpan.FromSeconds(&lt;span class=&#34;number&#34;&gt;5&lt;/span&gt;));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; ct = cts.Token;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// if this operation takes more than 5 seconds to run&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// a TaskCanceledException will be thrown&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; carRepository.GetByPlateNumberAsync(plateNumber, ct);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code&gt;CancellationTokenSource&lt;/code&gt; also supports to be linked to an existing &lt;code&gt;CancellationToken&lt;/code&gt; meaning that the underlying token can either be cancelled manually or by the linked token. This is usually useful to implement methods that offer some kind of timeout parameter without the caller having to use a &lt;code&gt;CancellationTokenSource&lt;/code&gt; directly.&lt;/p&gt;
&lt;p&gt;Let’s change the previously defined &lt;code&gt;SendAsync&lt;/code&gt; extension method to support both cancellation using a token or a timeout parameter that will throw a &lt;code&gt;TimeoutException&lt;/code&gt; if the waiting time is exceeded:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;MessageQueueExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task&amp;lt;JObject&amp;gt; &lt;span class=&#34;title&#34;&gt;SendAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; IMessageQueue queue,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        Guid correlationId,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        JObject content,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        TimeSpan timeout,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; tcs = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; TaskCompletionSource&amp;lt;JObject&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; cts = CancellationTokenSource.CreateLinkedTokenSource(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        cts.CancelAfter(timeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; _ = cts.Token.Register(() =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (ct.IsCancellationRequested)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;comment&#34;&gt;// if ct was cancelled we want to throw a TaskCanceledException&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                tcs.TrySetCanceled(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;comment&#34;&gt;// if ct wasn&amp;#x27;t cancelled but the cts token was triggered&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;comment&#34;&gt;// it can only mean the timeout parameter was exceeded&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            tcs.TrySetException(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; TimeoutException(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;string&#34;&gt;$&amp;quot;The queue took more than &amp;#x27;&lt;span class=&#34;subst&#34;&gt;&amp;#123;timeout&amp;#125;&lt;/span&gt;&amp;#x27; to return a message&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        queue.OnMessageReceived += OnMessageReceived;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        queue.OnErrorReceived += OnErrorReceived;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            queue.Send(correlationId, content);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; tcs.Task;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;finally&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            queue.OnMessageReceived -= OnMessageReceived;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            queue.OnErrorReceived -= OnErrorReceived;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;OnMessageReceived&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; _, OnMessageReceivedEventArgs args&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (args.CorrelationId == correlationId) &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                tcs.TrySetResult(args.Content);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;OnErrorReceived&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; _, OnErrorReceivedEventArgs args&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (args.CorrelationId == correlationId) &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                tcs.TrySetException(args.Exception);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;In this case, we now use the method &lt;code&gt;CreateLinkedTokenSource&lt;/code&gt; to ensure the &lt;code&gt;CancellationTokenSource&lt;/code&gt; token will be cancelled if the &lt;code&gt;ct&lt;/code&gt; parameter is cancelled or if a given timeout has passed. Because we are listening for the linked &lt;code&gt;cts&lt;/code&gt; token, inside the callback we must check if the &lt;code&gt;ct&lt;/code&gt; parameter was the cause of cancellation so we can either mark the task as canceled or faulted with a &lt;code&gt;TimeoutException&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;In this article I explained some of the reasons why Microsoft decided to replace the &lt;strong&gt;Asynchronous Programming Model (APM)&lt;/strong&gt; pattern for the &lt;strong&gt;Task-based Asynchronous Programming&lt;/strong&gt;, which is possible by using classes from the &lt;strong&gt;Task Parallel Library (TPL)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I also demonstrated how the classes &lt;code&gt;TaskCompletionSource&lt;/code&gt; and &lt;code&gt;CancellationTokenSource&lt;/code&gt; could be used to convert any asynchronous implementation to tasks, even if based on &lt;strong&gt;Event-based Asynchronous Pattern (EAP)&lt;/strong&gt;, ensuring threads won’t be blocked waiting for responses and even supporting the cancellation of running operations.&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="csharp" />
        <category term="dotnet" />
        <updated>2024-01-19T00:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2024/01/08/Dispose-pattern-in-NET/</id>
        <title>Dispose pattern in .NET</title>
        <link rel="alternate" href="https://code-corner.dev/2024/01/08/Dispose-pattern-in-NET/"/>
        <content type="html">&lt;p&gt;When working with .NET, you’ll often hear about the &lt;a href=&#34;https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/&#34;&gt;garbage collector&lt;/a&gt; and how it manages the allocation and release the application’s memory from the managed heap, making our life easier.&lt;/p&gt;
&lt;p&gt;This is true for the majority of objects but sometimes we have to work with unmanaged resources — files, network or database connections — that we must explicitly release since the garbage collector doesn’t know how to do the cleanup for us, despite being able to track the object that encapsulates the unmanaged resource.&lt;/p&gt;
&lt;p&gt;To help preventing memory leaks, .NET provides a simple and standard way to cleanup unmanaged resources called &lt;strong&gt;dispose pattern&lt;/strong&gt;, which consists in implementing the &lt;code&gt;IDisposable&lt;/code&gt; interface and calling Dispose method when resources aren’t needed, which is usually done via the &lt;code&gt;using&lt;/code&gt; keyword.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; file = File.OpenText(filePath))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; fileContent = file.ReadToEnd();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// equivalent code&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; file = File.OpenText(filePath)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; fileContent = file.ReadToEnd();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;finally&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    file.Dispose();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;When .NET Core 3.0 was released, Microsoft introduced the interface &lt;code&gt;IAsyncDisposable&lt;/code&gt; to allow for asynchronous cleanup operations by calling the &lt;code&gt;DisposeAsync&lt;/code&gt; method, usually done with &lt;code&gt;await using&lt;/code&gt; keywords.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; x = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; SomeAsyncDisposableClass())&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// do stuff&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// equivalent code&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; x = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; SomeAsyncDisposableClass();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// do stuff&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;finally&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; x.DisposeAsync();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If a class implements &lt;code&gt;IAsyncDisposable&lt;/code&gt; it’s usually a good practice to also implement &lt;code&gt;IDisposable&lt;/code&gt; despite not being a requirement. Not all code runs asynchronously, so this ensures the class is ready for both scenarios.&lt;/p&gt;
&lt;p&gt;In this article I’m going to demonstrate and explain step by step how to properly implement the &lt;strong&gt;dispose pattern&lt;/strong&gt; using both &lt;code&gt;IDisposable&lt;/code&gt; and &lt;code&gt;IAsyncDisposable&lt;/code&gt; interfaces.&lt;/p&gt;
&lt;h1 id=&#34;Example-scenario&#34;&gt;&lt;a href=&#34;#Example-scenario&#34; class=&#34;headerlink&#34; title=&#34;Example scenario&#34;&gt;&lt;/a&gt;Example scenario&lt;/h1&gt;&lt;p&gt;To use as an example, we are going to implement a class that wraps SQL Server connections and uses Dapper to simplify mappings and every time someone queries the database it will write the SQL instruction to the log. This provides a simple example of a class that holds both managed and unmanaged resources that can also be disposed asynchronously — SQL Server network connection.&lt;/p&gt;
&lt;p&gt;The class without any disposable implementations:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlServerQueryRunner&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; ILogger&amp;lt;SqlServerQueryRunner&amp;gt; _logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; SqlConnection _connection;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlServerQueryRunner&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        ILogger&amp;lt;SqlServerQueryRunner&amp;gt; logger,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; connectionString&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentNullException.ThrowIfNull(logger);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentNullException.ThrowIfNull(connectionString);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger = logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _connection = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; SqlConnection(connectionString);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ValueTask&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;QueryAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct, &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; sql, &lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; parameters = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Querying database: &amp;#123;Sql&amp;#125;&amp;quot;&lt;/span&gt;, sql);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// using Dapper for simplicity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _connection.QueryAsync&amp;lt;T&amp;gt;(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CommandDefinition(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            sql,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            parameters,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cancellationToken: ct&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As you can see, this class receives a connection string and creates a SQL Server connection. Because this class is the owner of the &lt;code&gt;SqlConnection&lt;/code&gt; instance, it must dispose it or else resources won’t be released.&lt;/p&gt;
&lt;h1 id=&#34;IDisposable-interface&#34;&gt;&lt;a href=&#34;#IDisposable-interface&#34; class=&#34;headerlink&#34; title=&#34;IDisposable interface&#34;&gt;&lt;/a&gt;IDisposable interface&lt;/h1&gt;&lt;p&gt;Implement the &lt;code&gt;IDisposable&lt;/code&gt; interface by disposing unmanaged resources inside the &lt;code&gt;Dispose&lt;/code&gt; method, in this case the SQL Server connection:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlServerQueryRunner&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IDisposable&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; ILogger&amp;lt;SqlServerQueryRunner&amp;gt; _logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; SqlConnection _connection;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Dispose&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _connection.Dispose();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;This code looks good and will work as expected, but there are some scenarios to consider:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Will the class be sealed?&lt;/strong&gt; If not, we must provide a way for extenders to cleanup their unmanaged resources, if any;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do I want to a safeguard from forgetting to dispose?&lt;/strong&gt; If yes, we must implement a class finalizer that will try to cleanup resources when garbage collected;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both scenarios are solved by creating a virtual Dispose method, to be overridden by extenders, that receives a flag to indicate if it’s being called from a dispose or finalizer.&lt;/p&gt;
&lt;p&gt;This usually means resources are explicitly disposed only when disposing, otherwise references are cleared and we assume unmanaged resources also have finalizers.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlServerQueryRunner&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IDisposable&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; ILogger&amp;lt;SqlServerQueryRunner&amp;gt; _logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; SqlConnection _connection;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ~SqlServerQueryRunner() =&amp;gt; Dispose(&lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Dispose&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Dispose(&lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        GC.SuppressFinalize(&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;virtual&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Dispose&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; disposing&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (disposing)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _connection?.Dispose();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _connection = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;You now may be asking: &lt;em&gt;But if we have finalizers that will be triggered when the class is garbage collected, why implement &lt;code&gt;IDisposable&lt;/code&gt;?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That’s a fair question with a simple answer: &lt;strong&gt;You want garbage collection to be as fast as possible because it is a synchronous operation that stops all processes while it’s running.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This also means unmanaged resources wouldn’t be released until garbage collection happened, which may take some time&lt;/strong&gt; — imagine a reference to a file that you didn’t need but the process would still be locking it, now you want to open it again but can’t, because the garbage collector haven’t run yet.&lt;/p&gt;
&lt;p&gt;If you look at the &lt;code&gt;Dispose&lt;/code&gt; method, you’ll realize there’s a call to &lt;code&gt;GC.SuppressFinalize&lt;/code&gt;, which is a method that actually tells the garbage collector: &lt;em&gt;look, this class has a finalizer but you can ignore it because it is irrelevant, the developer already released resources using the dispose pattern.&lt;/em&gt;&lt;/p&gt;
&lt;h1 id=&#34;IAsyncDisposable-interface&#34;&gt;&lt;a href=&#34;#IAsyncDisposable-interface&#34; class=&#34;headerlink&#34; title=&#34;IAsyncDisposable interface&#34;&gt;&lt;/a&gt;IAsyncDisposable interface&lt;/h1&gt;&lt;p&gt;Implementing the &lt;code&gt;IAsyncDisposable&lt;/code&gt; interface is very similar to &lt;code&gt;IDisposable&lt;/code&gt; with a small difference, we call &lt;code&gt;Dispose(false)&lt;/code&gt; inside the method &lt;code&gt;DisposeAsync&lt;/code&gt; to keep functional equivalence with the synchronous dispose pattern and further ensuring that finalizer code paths are still invoked. There’s no need to dispose resources synchronously if they were already disposed asynchronously.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlServerQueryRunner&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IDisposable&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;IAsyncDisposable&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; ILogger&amp;lt;SqlServerQueryRunner&amp;gt; _logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; SqlConnection _connection;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; ValueTask &lt;span class=&#34;title&#34;&gt;DisposeAsync&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; DisposeAsyncCore().ConfigureAwait(&lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Dispose(&lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        GC.SuppressFinalize(&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;virtual&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; ValueTask &lt;span class=&#34;title&#34;&gt;DisposeAsyncCore&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt;(_connection &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _connection.DisposeAsync().ConfigureAwait(&lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As a small note, if the class was sealed, the method &lt;code&gt;DisposeAsyncCore&lt;/code&gt; wouldn’t be needed and all code could be inside the &lt;code&gt;DisposeAsync&lt;/code&gt; method.&lt;/p&gt;
&lt;h1 id=&#34;ObjectDisposedException&#34;&gt;&lt;a href=&#34;#ObjectDisposedException&#34; class=&#34;headerlink&#34; title=&#34;ObjectDisposedException&#34;&gt;&lt;/a&gt;ObjectDisposedException&lt;/h1&gt;&lt;p&gt;When implementing a disposable class it’s also a good practice to throw an &lt;code&gt;ObjectDisposedException&lt;/code&gt; when it’s been disposed but some code is still trying to use it.&lt;/p&gt;
&lt;p&gt;This usually is as simple as creating a flag to be set by the &lt;code&gt;Dispose(bool)&lt;/code&gt; method and checking for it on all methods that may apply.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlServerQueryRunner&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IDisposable&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;IAsyncDisposable&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; ILogger&amp;lt;SqlServerQueryRunner&amp;gt; _logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; SqlConnection _connection;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; _disposed;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;virtual&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Dispose&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; disposing&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt;(_disposed)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (disposing)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _connection?.Dispose();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _connection = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _disposed = &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ValueTask&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;QueryAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct, &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; sql, &lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; parameters = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ObjectDisposedException.ThrowIf(_disposed, &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Querying database: &amp;#123;Sql&amp;#125;&amp;quot;&lt;/span&gt;, sql);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// using Dapper for simplicity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _connection.QueryAsync&amp;lt;T&amp;gt;(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CommandDefinition(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            sql,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            parameters,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cancellationToken: ct&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;In this article I explained the &lt;strong&gt;dispose pattern&lt;/strong&gt; in .NET, which is used to cleanup and release unmanaged resources from memory, either synchronously by implementing the &lt;code&gt;IDisposable&lt;/code&gt; interface, or asynchronously with &lt;code&gt;IAsyncDisposable&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For the example we used a simple wrapper for &lt;code&gt;SqlConnection&lt;/code&gt; that holds references to both unmanaged and managed resources, making sure both were properly cleared and an &lt;code&gt;ObjectDisposedException&lt;/code&gt; was thrown if the class was used after being disposed.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlServerQueryRunner&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IDisposable&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;IAsyncDisposable&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; ILogger&amp;lt;SqlServerQueryRunner&amp;gt; _logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; SqlConnection _connection;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; _disposed;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlServerQueryRunner&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        ILogger&amp;lt;SqlServerQueryRunner&amp;gt; logger,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; connectionString&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentNullException.ThrowIfNull(logger);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentNullException.ThrowIfNull(connectionString);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger = logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _connection = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; SqlConnection(connectionString);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;meta&#34;&gt;#&lt;span class=&#34;keyword&#34;&gt;region&lt;/span&gt; IDisposable&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ~SqlServerQueryRunner() =&amp;gt; Dispose(&lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Dispose&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Dispose(&lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        GC.SuppressFinalize(&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;virtual&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Dispose&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; disposing&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt;(_disposed)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (disposing)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _connection?.Dispose();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _connection = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _disposed = &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;meta&#34;&gt;#&lt;span class=&#34;keyword&#34;&gt;endregion&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;meta&#34;&gt;#&lt;span class=&#34;keyword&#34;&gt;region&lt;/span&gt; IAsyncDisposable&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; ValueTask &lt;span class=&#34;title&#34;&gt;DisposeAsync&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; DisposeAsyncCore().ConfigureAwait(&lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Dispose(&lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        GC.SuppressFinalize(&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;virtual&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; ValueTask &lt;span class=&#34;title&#34;&gt;DisposeAsyncCore&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_disposed)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_connection &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _connection.DisposeAsync().ConfigureAwait(&lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;meta&#34;&gt;#&lt;span class=&#34;keyword&#34;&gt;endregion&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ValueTask&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;QueryAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct, &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; sql, &lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; parameters = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ObjectDisposedException.ThrowIf(_disposed, &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Querying database: &amp;#123;Sql&amp;#125;&amp;quot;&lt;/span&gt;, sql);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// using Dapper for simplicity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _connection.QueryAsync&amp;lt;T&amp;gt;(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CommandDefinition(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            sql,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            parameters,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cancellationToken: ct&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;</content>
        <category term="dotnetcore" />
        <category term="csharp" />
        <category term="dotnet" />
        <updated>2024-01-08T00:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2023/12/28/Syntax-sugar-we-don%E2%80%99t-even-think-about-in-C/</id>
        <title>Syntax sugar we don’t even think about in C#</title>
        <link rel="alternate" href="https://code-corner.dev/2023/12/28/Syntax-sugar-we-don%E2%80%99t-even-think-about-in-C/"/>
        <content type="html">&lt;p&gt;When I finished my university degree, back in November 2010 (yes, I’m getting old), Microsoft wasn’t as open as it is today, but because they had a lot of education protocols we had access to a lot of their development tools for free — Visual Studio, SQL Server, Team Foundation Server, just to name a few.&lt;/p&gt;
&lt;p&gt;Because of this, C# was actually the language used to teach Object-Oriented Programming (OOP), design patterns and even deeper runtime concepts like garbage collection (an ode to the amazing book &lt;em&gt;“CLR via C#”&lt;/em&gt; from Jeffrey Richter — I learned so much).&lt;/p&gt;
&lt;p&gt;At the time, we also used a lot of Java, C, C++, LISP, and even Assembly, but C# earned a place in my heart.&lt;/p&gt;
&lt;p&gt;Every time I had to create something from scratch that could be run on Windows, C# and .NET Framework was always my first choice. I’m not sure if it was the language or how good Visual Studio was as an IDE compared to Eclipse or NetBeans, but I always found C# to be the most productive programming language I ever used.&lt;/p&gt;
&lt;p&gt;And this was between 2006 and 2010, a period were Microsoft released C# versions 3.0 and 4.0 which, in my opinion, completely overthrown their direct competition — Java.&lt;/p&gt;
&lt;p&gt;We had auto-implemented properties, extension methods, LINQ, implicit typed variables, anonymous types, query and lambda expressions, just to name a few.&lt;/p&gt;
&lt;p&gt;This changes aimed to keep developers focused in implementing business requirements instead of writing boilerplate code. Some may disagree, but for me it was the start of a new era in terms of programming productivity.&lt;/p&gt;
&lt;p&gt;If we look back, C# as come a long way, introducing new features every version and I think we can all agree C# 11.0 is an extremely mature and powerful language, suitable both for enterprise or simple applications.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Everyone now knows how much I love everything about C# — &lt;em&gt;almost :)&lt;/em&gt; — and since we are starting a new year, I’m feeling nostalgic and I think it is a good time to show how far we’ve come and how much syntax sugar we use nowadays without thinking how much time it really saves us.&lt;/p&gt;
&lt;h1 id=&#34;Auto-Properties-with-initializers&#34;&gt;&lt;a href=&#34;#Auto-Properties-with-initializers&#34; class=&#34;headerlink&#34; title=&#34;Auto-Properties (with initializers)&#34;&gt;&lt;/a&gt;Auto-Properties (with initializers)&lt;/h1&gt;&lt;p&gt;Ever since C# 1.0 we have properties, which behave like fields from a class or struct, but they are a special case of methods that can implement custom logic, not just get or set the field value.&lt;/p&gt;
&lt;p&gt;If we were to create a class &lt;code&gt;Car&lt;/code&gt; in C# 1.0 to hold some information like brand, model, license plate and even the number of doors, with a default of 4, it would look like this:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Car&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; _brand;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; _model;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; _licensePlate;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; _doors = &lt;span class=&#34;number&#34;&gt;4&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Brand&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; _brand;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _brand = &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Model&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; _model;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _model = &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; LicensePlate&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; _licensePlate;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _licensePlate = &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; Doors&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; _doors;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _doors = &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;With the introduction of auto-properties in C# 3.0 and initializers in C# 6.0, we can make the code much more compact and readable.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Car&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Brand &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Model &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; LicensePlate &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; Doors &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125; = &lt;span class=&#34;number&#34;&gt;4&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Using-statement-and-declaration&#34;&gt;&lt;a href=&#34;#Using-statement-and-declaration&#34; class=&#34;headerlink&#34; title=&#34;Using statement (and declaration)&#34;&gt;&lt;/a&gt;Using statement (and declaration)&lt;/h1&gt;&lt;p&gt;The using statement ensures that instances implementing &lt;code&gt;IDisposable&lt;/code&gt; are properly disposed.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; (IDisposable x = CreateSomeDisposable())&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// do stuff&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Without the &lt;code&gt;using&lt;/code&gt; statement, developers would need to always write a &lt;code&gt;try-finally&lt;/code&gt; block and check for &lt;code&gt;null&lt;/code&gt; to prevent exceptions.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;IDisposable x = CreateSomeDisposable();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// do stuff&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;finally&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!ReferenceEquals(x, &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;)) &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        x.Dispose();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;With the release of C# 8.0 we can now use the &lt;code&gt;using&lt;/code&gt; declaration, which simplifies even more the code, removing the need for brackets, disposing the instance at the end of the current scope.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; IDisposable x = CreateSomeDisposable();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// do stuff&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Foreach-loop&#34;&gt;&lt;a href=&#34;#Foreach-loop&#34; class=&#34;headerlink&#34; title=&#34;Foreach loop&#34;&gt;&lt;/a&gt;Foreach loop&lt;/h1&gt;&lt;p&gt;The &lt;code&gt;foreach&lt;/code&gt; keyword makes it easier to iterate over an &lt;code&gt;IEnumerable&lt;/code&gt; but did you know that, before C# 1.2, developers had to iterate over the enumerator and explicitly call the dispose method?&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;IEnumerable cars = GetAllCars();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IEnumerator carsEnumerator = cars.GetEnumerator();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; (carsEnumerator.MoveNext())&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Car car = (Car)carsEnumerator.Current;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// do stuff&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;finally&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    IDisposable disposable = carsEnumerator &lt;span class=&#34;keyword&#34;&gt;as&lt;/span&gt; IDisposable;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!ReferenceEquals(disposable, &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        disposable.Dispose();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;With the release of C# 2.0, Microsoft introduced generics and iterators as a first-class part of the language which nowadays means that iterating over any collection is just a simple use of the &lt;code&gt;foreach&lt;/code&gt; keyword:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;IEnumerable&amp;lt;Car&amp;gt; cars = GetAllCars();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (Car car &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; cars)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// do stuff&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Object-and-collection-initializers&#34;&gt;&lt;a href=&#34;#Object-and-collection-initializers&#34; class=&#34;headerlink&#34; title=&#34;Object and collection initializers&#34;&gt;&lt;/a&gt;Object and collection initializers&lt;/h1&gt;&lt;p&gt;Before C# 3.0, when initializing a new object you had to invoke the constructor followed by lines of assignment statements. Same with collections, were method &lt;code&gt;Add&lt;/code&gt; or indexers were used.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;Car car = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Car();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;car.Brand = &lt;span class=&#34;string&#34;&gt;&amp;quot;Audi&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;car.Model = &lt;span class=&#34;string&#34;&gt;&amp;quot;A8&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;car.LicensePlate = &lt;span class=&#34;string&#34;&gt;&amp;quot;ABCD-1234&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;car.Doors = &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;Car[] cars = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Car[&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;cars[&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;] = car;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;With object and collection initializers the code is more compact and scoped using brackets.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;Car car = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Car&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Brand = &lt;span class=&#34;string&#34;&gt;&amp;quot;Audi&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Model = &lt;span class=&#34;string&#34;&gt;&amp;quot;A8&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    LicensePlate = &lt;span class=&#34;string&#34;&gt;&amp;quot;ABCD-1234&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Doors = &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;Car[] cars = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Car[]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    car&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Implicitly-typed-variables-and-anonymous-types&#34;&gt;&lt;a href=&#34;#Implicitly-typed-variables-and-anonymous-types&#34; class=&#34;headerlink&#34; title=&#34;Implicitly typed variables (and anonymous types)&#34;&gt;&lt;/a&gt;Implicitly typed variables (and anonymous types)&lt;/h1&gt;&lt;p&gt;The release of C# 3.0 introduced some features, being one the possibility of creating anonymous types which are usually used to store read-only data into a temporary instance, being the class generated internally by the compiler.&lt;/p&gt;
&lt;p&gt;Because developers don’t know the class name since it will be generated at compile time, Microsoft had to provide a way to define the variable type, so they created the &lt;code&gt;var&lt;/code&gt; keyword that will tell the compiler it should infer the type.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; person = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    GivenName = &lt;span class=&#34;string&#34;&gt;&amp;quot;John&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Surname = &lt;span class=&#34;string&#34;&gt;&amp;quot;Doe&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Because &lt;code&gt;var&lt;/code&gt; can be used with anything, not just anonymous classes, some developers disagree it improves code legibility despite reducing the code that must be written, specially when defining generic variables.&lt;/p&gt;
&lt;p&gt;I personally believe that &lt;code&gt;var&lt;/code&gt; is an improvement to code legibility as long as the surrounding code is properly implemented, like giving proper names to variables and methods.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; car = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Car&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Brand = &lt;span class=&#34;string&#34;&gt;&amp;quot;Audi&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Model = &lt;span class=&#34;string&#34;&gt;&amp;quot;A8&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    LicensePlate = &lt;span class=&#34;string&#34;&gt;&amp;quot;ABCD-1234&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Doors = &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; cars = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt;[]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    car&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Lambda-expressions-and-Action-Func&#34;&gt;&lt;a href=&#34;#Lambda-expressions-and-Action-Func&#34; class=&#34;headerlink&#34; title=&#34;Lambda expressions (and Action&amp;#x2F;Func)&#34;&gt;&lt;/a&gt;Lambda expressions (and Action&amp;#x2F;Func&lt;T&gt;)&lt;/h1&gt;&lt;p&gt;To create anonymous functions that can be implicitly converted to delegates as long as the signature matches, Microsoft added the lambda declaration operator &lt;code&gt;=&amp;gt;&lt;/code&gt; and provided into the framework pre-existing delegate types like &lt;code&gt;Action&lt;/code&gt; for void results, or &lt;code&gt;Func&amp;lt;T&amp;gt;&lt;/code&gt; if a result was returned, with multiple overloads for variable input parameters.&lt;/p&gt;
&lt;p&gt;Before C# 3.0 developers had to define their own delegate classes and invokers had to be explicit by using the delegate keyword.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; IEnumerable&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;Filter&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IEnumerable&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt; values, FilterString filter&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; result = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; values)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt;(filter(&lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            result.Add(&lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; result;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;delegate&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;FilterString&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// usage&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; filterValues = Filter(values, &lt;span class=&#34;built_in&#34;&gt;delegate&lt;/span&gt;(&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; s)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; s.Contains(&lt;span class=&#34;string&#34;&gt;&amp;#x27;1&amp;#x27;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Using lambda expressions and pre-defined delegates, we don’t need to define our own and the code invoking &lt;code&gt;Filter&lt;/code&gt; is much compacter.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; IEnumerable&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;Filter&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IEnumerable&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt; values, Func&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;, &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt;&amp;gt; filter&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; result = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; values)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt;(filter(&lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            result.Add(&lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; result;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// usage&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; filterValues = Filter(values, s =&amp;gt; s.Contains(&lt;span class=&#34;string&#34;&gt;&amp;#x27;1&amp;#x27;&lt;/span&gt;));&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Extension-methods&#34;&gt;&lt;a href=&#34;#Extension-methods&#34; class=&#34;headerlink&#34; title=&#34;Extension methods&#34;&gt;&lt;/a&gt;Extension methods&lt;/h1&gt;&lt;p&gt;The release of C# 3.0 also introduced extension methods, an extremely powerful feature that allows to “add” methods to existing classes without modifying, extending or recompiling the class. They look like instance methods but are actually static methods that receive the instance as the first parameter and the compiler does the magic for us.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;StringExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;? ToNullableInt(&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt;(&lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;.ToInt();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ToInt&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;.Parse(&lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;, NumberStyles.None);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// usage&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; convertedValue = &lt;span class=&#34;string&#34;&gt;&amp;quot;12345&amp;quot;&lt;/span&gt;.ToNullableInt();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;Without extension methods the code would be more verbose:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;StringExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;? ToNullableInt(&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt;(&lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; ToInt(&lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ToInt&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;.Parse(&lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;, NumberStyles.None);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// usage&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; convertedValue = StringExtensions.ToNullableInt(&lt;span class=&#34;string&#34;&gt;&amp;quot;12345&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;LINQ-—-Language-Integrated-Query&#34;&gt;&lt;a href=&#34;#LINQ-—-Language-Integrated-Query&#34; class=&#34;headerlink&#34; title=&#34;LINQ — Language Integrated Query&#34;&gt;&lt;/a&gt;LINQ — Language Integrated Query&lt;/h2&gt;&lt;p&gt;I want to give a special note about LINQ, despite not being an integral part of the C# language but a framework library, it is a powerful demonstration of what can be done when using a lot of C# syntactic sugar features — the usage of generics, iterators, extension methods over &lt;code&gt;IEnumerable&lt;/code&gt;, lambda expressions, anonymous types and implicit typed local variables.&lt;/p&gt;
&lt;p&gt;Before C# 3.0, if we wanted to filter a collection cars with two doors and store a distinct collection of brands and models, we had to implement a class with an equality overload and use a HashSet to prevent duplicates.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CarBrandModelGroup&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IEquatable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;CarBrandModelGroup&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CarBrandModelGroup&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; brand,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; model&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Brand = brand;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Model = model;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Brand &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Model &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Equals&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CarBrandModelGroup other&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (ReferenceEquals(&lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;, other)) &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (ReferenceEquals(&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt;, other)) &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Brand == other.Brand &amp;amp;&amp;amp; Model == other.Model;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Equals&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; obj&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (ReferenceEquals(&lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;, obj)) &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (ReferenceEquals(&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt;, obj)) &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; obj.GetType() == GetType() &amp;amp;&amp;amp; Equals((CarBrandModelGroup)obj);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;GetHashCode&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;uint&lt;/span&gt; hc1;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (ReferenceEquals(Brand, &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            hc1 = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;else&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            hc1 = (&lt;span class=&#34;built_in&#34;&gt;uint&lt;/span&gt;)Brand.GetHashCode();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;uint&lt;/span&gt; hc2;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (ReferenceEquals(Model, &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            hc2 = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;else&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            hc2 = (&lt;span class=&#34;built_in&#34;&gt;uint&lt;/span&gt;)Model.GetHashCode();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; (&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;)(hc1 ^ hc2);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// the group by brand and model cars with doors == 2&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; carBrandModelsWithTwoDoors = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; HashSet&amp;lt;CarBrandModelGroup&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; car &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; cars)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (car.Doors == &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        carBrandModelsWithTwoDoors.Add(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CarBrandModelGroup(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            car.Brand,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            car.Model&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;With the introduction of LINQ, the code will be a simple use of the available methods while keeping in mind anonymous classes implement the default comparer, so no need for a custom class for grouping.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; carBrandModelsWithTwoDoors = cars.Where(c =&amp;gt; c.Doors == &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;).DistinctBy(c =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    c.Brand,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    c.Model&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;).ToArray();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;In this article I’ve shown how C# have evolved over the years to be a more productive and developer friendly language.&lt;/p&gt;
&lt;p&gt;Funny enough, when I started to write this article I wanted to cover major features that have been added over the years but then I realized just how big this language is and a single article would be too much, so I ended focusing more on C# 3.0.&lt;/p&gt;
&lt;p&gt;This clearly calls for more!&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="csharp" />
        <category term="dotnet" />
        <updated>2023-12-28T00:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2023/12/21/Configuration-providers-in-NET/</id>
        <title>Configuration providers in .NET</title>
        <link rel="alternate" href="https://code-corner.dev/2023/12/21/Configuration-providers-in-NET/"/>
        <content type="html">&lt;p&gt;One interesting feature of the .NET ecosystem is the ability to configure the application using &lt;code&gt;Microsoft.Extensions.Options&lt;/code&gt; library. It allows developers to easily manage and inject application settings from different sources, such as &lt;code&gt;appsettings.json&lt;/code&gt; files, environment variables, command-line arguments, or even custom sources.&lt;/p&gt;
&lt;p&gt;Using a SQL database as an example, in this article I’m going to explain how to create a custom provider for &lt;code&gt;Microsoft.Extensions.Options&lt;/code&gt; that reads key-valued configurations from a table that also ensure values are refreshed in-memory if the table content changes.&lt;/p&gt;
&lt;p&gt;If you want to use an approach for which you don’t have a provider available, like getting configurations from some custom API inside your company, you can easily use this example as a template to implement whatever requirements you may have.&lt;/p&gt;
&lt;h1 id=&#34;Solution-setup&#34;&gt;&lt;a href=&#34;#Solution-setup&#34; class=&#34;headerlink&#34; title=&#34;Solution setup&#34;&gt;&lt;/a&gt;Solution setup&lt;/h1&gt;&lt;p&gt;For starters, we’ll need a SQL database with a table to store the application settings. In this example, &lt;a href=&#34;https://github.com/gravity00/article-dotnet-configuration&#34;&gt;which I have available on GitHub&lt;/a&gt;, I’m going to use SQL Server (feel free to use your favorite database) and create an &lt;code&gt;ApplicationSettings&lt;/code&gt; table with two columns to store the keys and corresponding values.&lt;/p&gt;
&lt;figure class=&#34;highlight sql&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;create&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;table&lt;/span&gt; ApplicationSettings(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  [Key] nvarchar(&lt;span class=&#34;number&#34;&gt;256&lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;primary&lt;/span&gt; key,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  [&lt;span class=&#34;keyword&#34;&gt;Value&lt;/span&gt;] nvarchar(&lt;span class=&#34;number&#34;&gt;256&lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;null&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;To keep things simple, we are going to create a console application and use the NuGet &lt;code&gt;Microsoft.Extensions.Hosting&lt;/code&gt; to setup the host (dependency injection, logging and configurations) but this code is fully compatible with any application using ASP.NET Core 2 or later.&lt;/p&gt;
&lt;p&gt;Create a new console application and register the following NuGets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Extensions.Hosting&lt;/code&gt; — for hosting setup;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.data.SqlClient&lt;/code&gt; — to access our SQL Server database (or another provider if a different database);&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Dapper&lt;/code&gt; — optional, just to make it easier to map SQL results to C# entities;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also configure the &lt;code&gt;*.csproj&lt;/code&gt; file to include the application settings files when publishing.&lt;/p&gt;
&lt;figure class=&#34;highlight xml&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;Project&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;Sdk&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;Microsoft.NET.Sdk&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;PropertyGroup&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;OutputType&lt;/span&gt;&amp;gt;&lt;/span&gt;Exe&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;OutputType&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;TargetFramework&lt;/span&gt;&amp;gt;&lt;/span&gt;net8.0&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;TargetFramework&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;ImplicitUsings&lt;/span&gt;&amp;gt;&lt;/span&gt;enable&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;ImplicitUsings&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;PropertyGroup&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;ItemGroup&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;None&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;Include&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;*.config;*.json&amp;quot;&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;CopyToOutputDirectory&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;PreserveNewest&amp;quot;&lt;/span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;ItemGroup&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;ItemGroup&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;PackageReference&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;Include&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;Dapper&amp;quot;&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;Version&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;2.1.24&amp;quot;&lt;/span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;PackageReference&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;Include&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;Microsoft.Data.SqlClient&amp;quot;&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;Version&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;5.1.2&amp;quot;&lt;/span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;PackageReference&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;Include&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;Microsoft.Extensions.Hosting&amp;quot;&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;Version&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;8.0.0&amp;quot;&lt;/span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;ItemGroup&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;Project&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Add a new &lt;code&gt;appsettings.json&lt;/code&gt; file to the project and include some options, like the connection string to the SQL Server database and some custom settings — in this example we are going to output a simple message to the console.&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;ConnectionStrings&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;ArticleDotNetConfiguration&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;Data Source=localhost;Database=ArticleDotNetConfiguration;User Id=sa;Password=abcd1234;Encrypt=false;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;Example&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;Message&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;Hello world, from appsettings.json!&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Create a class representing the example options.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;record&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ExampleOptions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Message &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Open the &lt;code&gt;Program.cs&lt;/code&gt; file, create a host with pre-configured defaults, configure the &lt;code&gt;ExampleOptions&lt;/code&gt; and output the message that was loaded from the &lt;code&gt;appsettings.json&lt;/code&gt; file.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; builder = Host.CreateApplicationBuilder(args);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.Configure&amp;lt;ExampleOptions&amp;gt;(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    builder.Configuration.GetSection(&lt;span class=&#34;string&#34;&gt;&amp;quot;Example&amp;quot;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; host = builder.Build();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; options = host.Services.GetRequiredService&amp;lt;IOptions&amp;lt;ExampleOptions&amp;gt;&amp;gt;().Value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;Console.WriteLine(options.Message);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Running the application, the message stored in the &lt;code&gt;appsettings.json&lt;/code&gt; file should be displayed.&lt;/p&gt;
&lt;img src=&#34;/2023/12/21/Configuration-providers-in-NET/01_hello_world_appsettings_json.png&#34; class=&#34;&#34;&gt;

&lt;h1 id=&#34;Configuration-provider&#34;&gt;&lt;a href=&#34;#Configuration-provider&#34; class=&#34;headerlink&#34; title=&#34;Configuration provider&#34;&gt;&lt;/a&gt;Configuration provider&lt;/h1&gt;&lt;p&gt;To create a custom configuration provider you need to implement two interfaces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IConfigurationSource&lt;/code&gt; — will be added to the configuration manager and is used to store options (like a connection string) and build provider instances.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IConfigurationProvider&lt;/code&gt; — knows how to load the application settings and has methods to get or set configuration values by a given key. Providers usually extend the class &lt;code&gt;ConfigurationProvider&lt;/code&gt; which makes it easier to store settings in-memory.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Create a new class &lt;code&gt;SqlServerConfigurationSource&lt;/code&gt;, add a property for the connection string and implement the interface &lt;code&gt;IConfigurationSource&lt;/code&gt; without any logic, for now.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlServerConfigurationSource&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IConfigurationSource&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; ConnectionString &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; IConfigurationProvider &lt;span class=&#34;title&#34;&gt;Build&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IConfigurationBuilder builder&lt;/span&gt;)&lt;/span&gt; =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; NotImplementedException();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Create a new class &lt;code&gt;SqlServerConfigurationProvider&lt;/code&gt;, passing the configuration source as a constructor parameter and extending the class &lt;code&gt;ConfigurationProvider&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Override the &lt;code&gt;Load&lt;/code&gt; method, reading application settings from the SQL table and store the results into the &lt;code&gt;Data&lt;/code&gt; dictionary.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlServerConfigurationProvider&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    SqlServerConfigurationSource source&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;) : ConfigurationProvider&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Load&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; connection = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; SqlConnection(source.ConnectionString);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        connection.Open();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Data = connection.Query&amp;lt;(&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Key, &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Value)&amp;gt;(&lt;span class=&#34;string&#34;&gt;@&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;select &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;    [Key],&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;    [Value]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;from ApplicationSettings&amp;quot;&lt;/span&gt;).ToDictionary(e =&amp;gt; e.Key, e =&amp;gt; e.Value);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Reopen the &lt;code&gt;SqlServerConfigurationSource.cs&lt;/code&gt; file and implement the &lt;code&gt;Build&lt;/code&gt; method, returning a new instance of the provider.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlServerConfigurationSource&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IConfigurationSource&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; ConnectionString &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; IConfigurationProvider &lt;span class=&#34;title&#34;&gt;Build&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IConfigurationBuilder builder&lt;/span&gt;)&lt;/span&gt; =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; SqlServerConfigurationProvider(&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Open the &lt;code&gt;Program.cs&lt;/code&gt; file and add the SQL Server configuration source to the host builder. Don’t forget to read the connection string from the existing sources.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; builder = Host.CreateApplicationBuilder(args);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; connectionString = builder.Configuration.GetConnectionString(&lt;span class=&#34;string&#34;&gt;&amp;quot;ArticleDotNetConfiguration&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Configuration.Add&amp;lt;SqlServerConfigurationSource&amp;gt;(source =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    source.ConnectionString = connectionString;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.Configure&amp;lt;ExampleOptions&amp;gt;(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    builder.Configuration.GetSection(&lt;span class=&#34;string&#34;&gt;&amp;quot;Example&amp;quot;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Since the SQL Server provider was the last source added to the configuration manager, it will have priority over the existing ones. Let’s try to override the message by adding the key &lt;code&gt;Example:Message&lt;/code&gt; with a value like &lt;code&gt;Hello world, from database!&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight sql&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;insert&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;into&lt;/span&gt; ApplicationSettings &lt;span class=&#34;keyword&#34;&gt;values&lt;/span&gt; (&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;string&#34;&gt;&amp;#x27;Example:Message&amp;#x27;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;string&#34;&gt;&amp;#x27;Hello world, from database!&amp;#x27;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Running the application, the message stored in the &lt;code&gt;ApplicationSettings&lt;/code&gt; table should be displayed.&lt;/p&gt;
&lt;img src=&#34;/2023/12/21/Configuration-providers-in-NET/02_hello_world_database.png&#34; class=&#34;&#34;&gt;

&lt;h1 id=&#34;Reloading-data&#34;&gt;&lt;a href=&#34;#Reloading-data&#34; class=&#34;headerlink&#34; title=&#34;Reloading data&#34;&gt;&lt;/a&gt;Reloading data&lt;/h1&gt;&lt;p&gt;In our previous code, we were getting an &lt;code&gt;IOptions&lt;/code&gt; from the container to get the application settings. This is fine but what if the developer is using an &lt;code&gt;IOptionsMonitor&lt;/code&gt; to always get the latest configurations?&lt;/p&gt;
&lt;p&gt;The current provider implementation never updates the &lt;code&gt;Data&lt;/code&gt; property after the initial load, so we let’s change that.&lt;/p&gt;
&lt;p&gt;There are multiple ways to detect data changes from a SQL Server database, but for simplicity we are going to implement a simple worker that will be constantly loading application settings, compare what’s in memory and if changes are detected, invoke the base method &lt;code&gt;OnReload&lt;/code&gt; that will trigger a refresh in all listeners.&lt;/p&gt;
&lt;p&gt;Let’s start by changing the &lt;code&gt;Program.cs&lt;/code&gt; file and create a task that will be writing the message at intervals so we’ll see a different message when we update the table. Don’t forget to use an &lt;code&gt;IOptionsMonitor&lt;/code&gt; instead of an &lt;code&gt;IOptions&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; builder = Host.CreateApplicationBuilder(args);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; connectionString = builder.Configuration.GetConnectionString(&lt;span class=&#34;string&#34;&gt;&amp;quot;ArticleDotNetConfiguration&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Configuration.Add&amp;lt;SqlServerConfigurationSource&amp;gt;(source =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    source.ConnectionString = connectionString;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;builder.Services.Configure&amp;lt;ExampleOptions&amp;gt;(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    builder.Configuration.GetSection(&lt;span class=&#34;string&#34;&gt;&amp;quot;Example&amp;quot;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; host = builder.Build();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; appOptions = host.Services.GetRequiredService&amp;lt;IOptionsMonitor&amp;lt;ExampleOptions&amp;gt;&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; Task.Run(&lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; () =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;do&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; options = appOptions.CurrentValue;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Console.WriteLine(options.Message);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; Task.Delay(&lt;span class=&#34;number&#34;&gt;5&lt;/span&gt;_000);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125; &lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; (&lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Open the provider class and do the following changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add a private &lt;code&gt;CancellationTokenSource&lt;/code&gt; variable that will be used to cancel the worker execution;&lt;/li&gt;
&lt;li&gt;Add a private &lt;code&gt;Task&lt;/code&gt; variable that will be the responsible to refresh the application settings when they change;&lt;/li&gt;
&lt;li&gt;Extract the logic that loads the data into a private method, so both Load method and worker can use that logic — change it to asynchronous code and do a comparison with previous loaded data, so method &lt;code&gt;OnReload&lt;/code&gt; can be triggered;&lt;/li&gt;
&lt;li&gt;Change the &lt;code&gt;Load&lt;/code&gt; method to use the extracted logic and create a worker that will refresh data at intervals — don’t forget to add some exception handling code;&lt;/li&gt;
&lt;li&gt;Implement the disposable pattern to release unmanaged resources;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;SqlServerConfigurationProvider&lt;/code&gt; should now be as follows:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;97&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;98&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;99&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;100&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;101&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;102&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;103&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;104&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;105&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;106&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;107&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;108&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;109&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;110&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;111&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;112&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;113&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlServerConfigurationProvider&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    SqlServerConfigurationSource source&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;) : ConfigurationProvider, IDisposable&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; CancellationTokenSource _cts;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; Task _refreshWorker;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;meta&#34;&gt;#&lt;span class=&#34;keyword&#34;&gt;region&lt;/span&gt; IDisposable&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ~SqlServerConfigurationProvider() =&amp;gt; Dispose(&lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Dispose&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Dispose(&lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        GC.SuppressFinalize(&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;virtual&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Dispose&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; disposing&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (disposing)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _cts?.Cancel();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_refreshWorker &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    _refreshWorker.ConfigureAwait(&lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        .GetAwaiter().GetResult();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (OperationCanceledException)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;comment&#34;&gt;// expected exception due to cancellation&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (Exception e)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    Debug.WriteLine(&lt;span class=&#34;string&#34;&gt;$&amp;quot;Unhandled exception when waiting for the worker to stop: &lt;span class=&#34;subst&#34;&gt;&amp;#123;e&amp;#125;&lt;/span&gt;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _cts?.Dispose();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _refreshWorker = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _cts = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;meta&#34;&gt;#&lt;span class=&#34;keyword&#34;&gt;endregion&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Load&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        LoadAsync(CancellationToken.None).ConfigureAwait(&lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .GetAwaiter().GetResult();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_cts &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;) &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _cts = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CancellationTokenSource();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; ct = _cts.Token;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _refreshWorker ??= Task.Run(&lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; () =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;do&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; Task.Delay(&lt;span class=&#34;number&#34;&gt;15&lt;/span&gt;_000, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; LoadAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (Exception e)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    Debug.WriteLine(&lt;span class=&#34;string&#34;&gt;$&amp;quot;Unhandled exception when refreshing database settings: &lt;span class=&#34;subst&#34;&gt;&amp;#123;e&amp;#125;&lt;/span&gt;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125; &lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; (!ct.IsCancellationRequested);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;LoadAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Dictionary&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;, &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt; currentData;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; connection = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; SqlConnection(source.ConnectionString))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; connection.OpenAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            currentData = (&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; connection.QueryAsync&amp;lt;(&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Key, &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Value)&amp;gt;(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CommandDefinition(&lt;span class=&#34;string&#34;&gt;@&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;select &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;    [Key],&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;    [Value]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;from ApplicationSettings&amp;quot;&lt;/span&gt;, cancellationToken: ct))).ToDictionary(e =&amp;gt; e.Key, e =&amp;gt; e.Value);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (HasSameData(currentData))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Data = currentData;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        OnReload();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;HasSameData&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;Dictionary&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;, &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt; currentData&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (Data.Count != currentData.Count)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; (key, &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; currentData)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!Data.TryGetValue(key, &lt;span class=&#34;keyword&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; previousValue) || previousValue != &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you now start the application and update or delete the value while running, you should see the task writing different messages.&lt;/p&gt;
&lt;figure class=&#34;highlight sql&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;-- update the settings&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;update&lt;/span&gt; ApplicationSettings&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  [&lt;span class=&#34;keyword&#34;&gt;Value&lt;/span&gt;] &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;#x27;Hello world, from DB!&amp;#x27;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  [Key] &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;#x27;Example:Message&amp;#x27;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;-- or delete them&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;delete&lt;/span&gt; ApplicationSettings&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  [Key] &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;#x27;Example:Message&amp;#x27;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;img src=&#34;/2023/12/21/Configuration-providers-in-NET/03_hell_world_final.png&#34; class=&#34;&#34;&gt;

&lt;h1 id=&#34;Reusing-the-provider&#34;&gt;&lt;a href=&#34;#Reusing-the-provider&#34; class=&#34;headerlink&#34; title=&#34;Reusing the provider&#34;&gt;&lt;/a&gt;Reusing the provider&lt;/h1&gt;&lt;p&gt;We now have a provider ready to read application settings from a SQL Server database, but I’m sure you are thinking it clearly could be more generic and just work with any SQL database.&lt;/p&gt;
&lt;p&gt;Your line of thought is correct, we can easily do that just by changing the options from a connection string to having a connection factory, adding a property for the SQL code and even a &lt;code&gt;TimeSpan&lt;/code&gt; for refresh interval. We should also add an exception handling callback (instead of writing into the debugger) and create extension methods to make registration into the configuration manager more readable.&lt;/p&gt;
&lt;p&gt;The final code, &lt;a href=&#34;https://github.com/gravity00/article-dotnet-configuration&#34;&gt;which is available on GitHub&lt;/a&gt;, could be the as follows:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;97&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;98&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;99&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;100&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;101&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;102&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;103&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;104&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;105&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;106&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;107&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;108&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;109&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;110&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;111&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;112&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;113&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;114&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;115&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;116&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;117&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;118&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;119&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;120&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;121&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;122&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;123&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;124&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;125&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;126&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;127&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;128&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;129&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;130&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;131&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;132&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;133&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;134&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;135&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;136&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;137&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;138&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;139&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;140&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;141&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;142&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;143&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;144&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;145&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;146&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;147&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;148&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;149&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;150&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;151&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;152&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;153&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;154&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;155&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;156&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;157&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;158&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;159&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;160&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;161&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;162&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;163&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;164&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;165&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;166&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;167&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;168&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;169&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;170&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;171&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;172&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;173&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;174&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;175&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;176&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;177&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;178&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;179&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;180&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;181&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;182&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;183&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;184&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;185&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;186&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;187&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;188&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;189&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;190&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;191&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;192&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;193&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;194&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;195&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;196&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;197&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlExceptionContext&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Exception Exception &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; SqlConfigurationProvider Provider &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlConfigurationBuilderExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; SqlExceptionHandlerKey = &lt;span class=&#34;string&#34;&gt;&amp;quot;SqlExceptionHandler&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; IConfigurationBuilder &lt;span class=&#34;title&#34;&gt;AddSql&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; IConfigurationBuilder builder,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        Func&amp;lt;DbConnection&amp;gt; connectionFactory,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; sql = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        TimeSpan? refreshInterval = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentNullException.ThrowIfNull(builder);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentNullException.ThrowIfNull(connectionFactory);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; builder.Add&amp;lt;SqlConfigurationSource&amp;gt;(source =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            source.ConnectionFactory = connectionFactory;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt;(sql &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                source.Sql = sql;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (refreshInterval &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                source.RefreshInterval = refreshInterval.Value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; IConfigurationBuilder &lt;span class=&#34;title&#34;&gt;SetSqlExceptionHandler&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; IConfigurationBuilder builder,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        Action&amp;lt;SqlExceptionContext&amp;gt; handler&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentNullException.ThrowIfNull(builder);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        builder.Properties[SqlExceptionHandlerKey] = handler;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; builder;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; Action&amp;lt;SqlExceptionContext&amp;gt; &lt;span class=&#34;title&#34;&gt;GetSqlExceptionHandler&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; IConfigurationBuilder builder&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ArgumentNullException.ThrowIfNull(builder);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; builder.Properties.TryGetValue(SqlExceptionHandlerKey, &lt;span class=&#34;keyword&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ? &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;as&lt;/span&gt; Action&amp;lt;SqlExceptionContext&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            : &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlConfigurationSource&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IConfigurationSource&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Func&amp;lt;DbConnection&amp;gt; ConnectionFactory &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Sql &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125; = &lt;span class=&#34;string&#34;&gt;@&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;select &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;    [Key],&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;    [Value]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;from ApplicationSettings&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; TimeSpan RefreshInterval &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125; = TimeSpan.FromSeconds(&lt;span class=&#34;number&#34;&gt;15&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; IConfigurationProvider &lt;span class=&#34;title&#34;&gt;Build&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IConfigurationBuilder builder&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; exceptionHandler = builder.GetSqlExceptionHandler() ?? (ctx =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Debug.WriteLine(&lt;span class=&#34;string&#34;&gt;$&amp;quot;Unhandled SQL exception: &lt;span class=&#34;subst&#34;&gt;&amp;#123;ctx.Exception&amp;#125;&lt;/span&gt;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; SqlConfigurationProvider(&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt;, exceptionHandler);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SqlConfigurationProvider&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    SqlConfigurationSource source,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    Action&amp;lt;SqlExceptionContext&amp;gt; exceptionHandler&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;) : ConfigurationProvider, IDisposable&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; CancellationTokenSource _cts;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; Task _refreshWorker;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;meta&#34;&gt;#&lt;span class=&#34;keyword&#34;&gt;region&lt;/span&gt; IDisposable&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ~SqlConfigurationProvider() =&amp;gt; Dispose(&lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Dispose&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Dispose(&lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        GC.SuppressFinalize(&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;virtual&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Dispose&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; disposing&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (disposing)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _cts?.Cancel();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_refreshWorker &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    _refreshWorker.ConfigureAwait(&lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        .GetAwaiter().GetResult();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (OperationCanceledException)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;comment&#34;&gt;// expected exception due to cancellation&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (Exception e)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    exceptionHandler(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; SqlExceptionContext&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        Exception = e,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        Provider = &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _cts?.Dispose();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _refreshWorker = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _cts = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;meta&#34;&gt;#&lt;span class=&#34;keyword&#34;&gt;endregion&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Load&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        LoadAsync(CancellationToken.None).ConfigureAwait(&lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .GetAwaiter().GetResult();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_cts &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _cts = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CancellationTokenSource();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; ct = _cts.Token;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _refreshWorker ??= Task.Run(&lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; () =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;do&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; Task.Delay(source.RefreshInterval, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; LoadAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (Exception e)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    exceptionHandler(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; SqlExceptionContext&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        Exception = e,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        Provider = &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125; &lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; (!ct.IsCancellationRequested);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;LoadAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Dictionary&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;, &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt; currentData;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; connection = source.ConnectionFactory())&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; connection.OpenAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            currentData = (&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; connection.QueryAsync&amp;lt;(&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Key, &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Value)&amp;gt;(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CommandDefinition(source.Sql, cancellationToken: ct)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            )).ToDictionary(e =&amp;gt; e.Key, e =&amp;gt; e.Value);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (HasSameData(currentData))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Data = currentData;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        OnReload();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;HasSameData&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;Dictionary&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;, &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt; currentData&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (Data.Count != currentData.Count)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; (key, &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; currentData)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!Data.TryGetValue(key, &lt;span class=&#34;keyword&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; previousValue) || previousValue != &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;In this article I explained how to create a custom configuration provider for the &lt;code&gt;Microsoft.Extensions.Options&lt;/code&gt; library, that could be used in any .NET application that uses &lt;code&gt;Microsoft.Extensions.Hosting&lt;/code&gt;, including ASP.NET Core applications.&lt;/p&gt;
&lt;p&gt;I used a SQL Server database as an example, but could be easily anything, like getting settings from an internal API and even Windows registry.&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="aspnetcore" />
        <category term="csharp" />
        <updated>2023-12-21T00:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2023/12/14/C-%E2%80%98is-null%E2%80%99-vs-%E2%80%98-null%E2%80%99/</id>
        <title>C# ‘is null’ vs ‘== null’</title>
        <link rel="alternate" href="https://code-corner.dev/2023/12/14/C-%E2%80%98is-null%E2%80%99-vs-%E2%80%98-null%E2%80%99/"/>
        <content type="html">&lt;p&gt;When C# 7.0 was officially released in March 2017 it introduced &lt;a href=&#34;https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-70&#34;&gt;several new features&lt;/a&gt; to make the life of developers easier, like tuples and deconstruction, local functions, expression body definitions and, the focus of this article, &lt;strong&gt;pattern matching&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;One of the advantages of pattern matching is a more concise syntax for testing expressions and taking actions when there’s a match, increasing the readability and correctness of your code.&lt;/p&gt;
&lt;p&gt;Checking for nulls is one of the most common usages. Before C# 7.0, if a developer wanted to check if a given value was null, usually the equality comparison would be used.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;PersonExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;FullName&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; Person person&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (person == &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ArgumentNullException(&lt;span class=&#34;keyword&#34;&gt;nameof&lt;/span&gt;(person));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;$&amp;quot;&lt;span class=&#34;subst&#34;&gt;&amp;#123;person.GivenName&amp;#125;&lt;/span&gt; &lt;span class=&#34;subst&#34;&gt;&amp;#123;person.Surname&amp;#125;&lt;/span&gt;&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;With pattern matching, the extension method would be using the &lt;code&gt;is&lt;/code&gt; operator and the &lt;code&gt;null&lt;/code&gt; constant to check if the person variable is null.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;PersonExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;FullName&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; Person person&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (person &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ArgumentNullException(&lt;span class=&#34;keyword&#34;&gt;nameof&lt;/span&gt;(person));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;$&amp;quot;&lt;span class=&#34;subst&#34;&gt;&amp;#123;person.GivenName&amp;#125;&lt;/span&gt; &lt;span class=&#34;subst&#34;&gt;&amp;#123;person.Surname&amp;#125;&lt;/span&gt;&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The change is so small that you are probably questioning if changing the way you code is really worthy for such a small readability improvement, to have the same results?&lt;/p&gt;
&lt;p&gt;And you are right in thinking that way since that’s probably true for 99% of use cases (if not 99.999%) but what if you really want to be sure it works every time?&lt;/p&gt;
&lt;p&gt;You see, reference types can overload the &lt;code&gt;==&lt;/code&gt; operator and, if the developer decides to always return false when comparing to null, the first example would throw a &lt;code&gt;NullReferenceException&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let’s test that by overloading the &lt;code&gt;==&lt;/code&gt; operator to always return false, whatever the parameters received:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Person&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; GivenName &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Surname &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;operator&lt;/span&gt; ==(Person left, Person right) =&amp;gt; &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;operator&lt;/span&gt; !=(Person left, Person right) =&amp;gt; !(left == right);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;PersonExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;FullName&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; Person person&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (person == &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ArgumentNullException(&lt;span class=&#34;keyword&#34;&gt;nameof&lt;/span&gt;(person));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;$&amp;quot;&lt;span class=&#34;subst&#34;&gt;&amp;#123;person.GivenName&amp;#125;&lt;/span&gt; &lt;span class=&#34;subst&#34;&gt;&amp;#123;person.Surname&amp;#125;&lt;/span&gt;&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If we now call the extension method &lt;code&gt;FullName&lt;/code&gt; with a null argument a &lt;code&gt;NullReferenceException&lt;/code&gt; will be thrown instead of the expected &lt;code&gt;ArgumentNullException&lt;/code&gt;. This happens because the compiler knows there’s an equality overload, calls it and gets false, so if won’t enter the if statement.&lt;/p&gt;
&lt;img src=&#34;/2023/12/14/C-%E2%80%98is-null%E2%80%99-vs-%E2%80%98-null%E2%80%99/01_null_reference_exception.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;If you look at the generated IL Code, you’ll see the Person equality operator being called.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;// [36 5 - 36 6]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0000: nop&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// [37 9 - 37 28]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0001: ldarg.0      // person&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0002: ldnull&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0003: call         bool Person::op_Equality(class Person, class Person)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0008: stloc.0      // V_0&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// [37 29 - 37 77]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0009: ldloc.0      // V_0&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_000a: brfalse.s    IL_0017&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_000c: ldstr        &amp;quot;person&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0011: newobj       instance void [System.Runtime]System.ArgumentNullException::.ctor(string)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0016: throw&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0017: ldarg.0      // person&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0018: callvirt     instance string Person::get_GivenName()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_001d: ldstr        &amp;quot; &amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0022: ldarg.0      // person&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0023: callvirt     instance string Person::get_Surname()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0028: call         string [System.Runtime]System.String::Concat(string, string, string)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// [40 5 - 40 6]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_002d: stloc.1      // V_1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_002e: br.s         IL_0030&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0030: ldloc.1      // V_1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0031: ret&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Before C# 7.0 if you wanted to be sure the &lt;code&gt;==&lt;/code&gt; operator overload was ignored, you had to use &lt;code&gt;object.ReferenceEquals&lt;/code&gt; to compare the object reference to null.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;PersonExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;FullName&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; Person person&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (ReferenceEquals(person, &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;)) &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ArgumentNullException(&lt;span class=&#34;keyword&#34;&gt;nameof&lt;/span&gt;(person));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;$&amp;quot;&lt;span class=&#34;subst&#34;&gt;&amp;#123;person.GivenName&amp;#125;&lt;/span&gt; &lt;span class=&#34;subst&#34;&gt;&amp;#123;person.Surname&amp;#125;&lt;/span&gt;&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;But when using pattern matching the compiler always generates code that compares to null, ignoring any &lt;code&gt;==&lt;/code&gt; operator overload. This means you have the same behavior as using the &lt;code&gt;object.ReferenceEquals&lt;/code&gt; method without writing so much code.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Person&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; GivenName &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Surname &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;operator&lt;/span&gt; ==(Person left, Person right) =&amp;gt; &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;operator&lt;/span&gt; !=(Person left, Person right) =&amp;gt; !(left == right);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;PersonExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;FullName&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; Person person&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (person &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ArgumentNullException(&lt;span class=&#34;keyword&#34;&gt;nameof&lt;/span&gt;(person));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;$&amp;quot;&lt;span class=&#34;subst&#34;&gt;&amp;#123;person.GivenName&amp;#125;&lt;/span&gt; &lt;span class=&#34;subst&#34;&gt;&amp;#123;person.Surname&amp;#125;&lt;/span&gt;&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;This ensures the method &lt;code&gt;FullName&lt;/code&gt; always throws &lt;code&gt;ArgumentNullException&lt;/code&gt; when a null reference is received.&lt;/p&gt;
&lt;img src=&#34;/2023/12/14/C-%E2%80%98is-null%E2%80%99-vs-%E2%80%98-null%E2%80%99/02_argument_null_exception.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Looking at the generated IL Code, we can see the &lt;code&gt;person&lt;/code&gt; reference is being compared to null using the &lt;code&gt;ceq&lt;/code&gt; instruction (this is exactly the same code if &lt;code&gt;object.ReferenceEquals&lt;/code&gt; was being used — it would be inlined).&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;// [36 5 - 36 6]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0000: nop&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// [37 9 - 37 43]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0001: ldarg.0      // person&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0002: ldnull&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0003: ceq&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0005: stloc.0      // V_0&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0006: ldloc.0      // V_0&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0007: brfalse.s    IL_0014&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// [37 44 - 37 92]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0009: ldstr        &amp;quot;person&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_000e: newobj       instance void [System.Runtime]System.ArgumentNullException::.ctor(string)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0013: throw&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// [39 9 - 39 55]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0014: ldarg.0      // person&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0015: callvirt     instance string Person::get_GivenName()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_001a: ldstr        &amp;quot; &amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_001f: ldarg.0      // person&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0020: callvirt     instance string Person::get_Surname()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_0025: call         string [System.Runtime]System.String::Concat(string, string, string)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_002a: stloc.1      // V_1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_002b: br.s         IL_002d&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// [40 5 - 40 6]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_002d: ldloc.1      // V_1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;IL_002e: ret&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;In this article I explained why using pattern matching for null checks is preferred to using the equality operator.&lt;/p&gt;
&lt;p&gt;Not only it’s a small readability improvement but it also ensures the object reference is always compared to null even if the class has overloaded the &lt;code&gt;==&lt;/code&gt; operator (same behavior as using the &lt;code&gt;object.ReferenceEquals&lt;/code&gt; method but without writing so much code).&lt;/p&gt;
&lt;p&gt;Even if the equality operator overload is properly implemented, we can also consider this a small performance increase because executing the ceq operation is faster that a call to the class equality operator.&lt;/p&gt;
&lt;p&gt;As stated before, this may not affect 99% of your use cases, but for such small a small change in the way you code, why not?&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="csharp" />
        <category term="dotnet" />
        <updated>2023-12-14T00:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2023/12/04/NET-%E2%80%94-DevOps-and-Entity-Framework-Core/</id>
        <title>.NET — DevOps and Entity Framework Core</title>
        <link rel="alternate" href="https://code-corner.dev/2023/12/04/NET-%E2%80%94-DevOps-and-Entity-Framework-Core/"/>
        <content type="html">&lt;p&gt;Most .NET developers either have used Entity Framework Core or eventually will, because it is one of the most known and flexible ORM frameworks to access databases in the .NET ecosystem, directly supported by Microsoft and the Open Source community.&lt;/p&gt;
&lt;p&gt;In this article I’m going to explain how you can create a console application that will check if migrations are missing from the database and apply them accordingly. This is an approach I’ve been using ever since Microsoft released .NET Core 1 RC 1 (&lt;a href=&#34;https://medium.com/@joaoprsimoes/net-creating-advanced-console-applications-c99d58216d36&#34;&gt;at the time I even created an open-source library to facilitate console hosting&lt;/a&gt;, now deprecated because we can use &lt;code&gt;Microsoft.Extensions.Hosting&lt;/code&gt;).&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Ever since its inception, one of the main features supported by Entity Framework is the concept of &lt;strong&gt;database migrations&lt;/strong&gt; in code-first scenarios which, in a simplified way, work as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Model entities representing tables are mapped into the &lt;code&gt;DbContext&lt;/code&gt;, were table and column names, data sizes, foreign keys, indexes, and so on, are defined;&lt;/li&gt;
&lt;li&gt;Entity Framework tools are &lt;a href=&#34;https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli#install-the-tools&#34;&gt;installed into the project&lt;/a&gt; (either the .NET Core CLI tools for cross platform or Package Manager Console tools for Visual Studio integration);&lt;/li&gt;
&lt;li&gt;A class implementing &lt;code&gt;IDesignTimeDbContextFactory&lt;/code&gt; is created and will configure the &lt;code&gt;DbContext&lt;/code&gt; when Entity Framework tools are run — provider to use, connection string, etc. This reduces the number of parameters that need to be passed when using the CLI&amp;#x2F;PMC tools.&lt;/li&gt;
&lt;li&gt;When adding a new migration, the tools are going to use the factory to create the &lt;code&gt;DbContext&lt;/code&gt;, compare the mappings to the current database schema, see what’s different and create a migration class with a bunch of C# operations that will change the schema accordingly (like creating a new index, adding a new column, dropping a table). The migration will also have a method for the inverse operations (drop the index, remove the column, recreate the table) that can be used to revert changes;&lt;/li&gt;
&lt;li&gt;The tools can also be run to update the database schema, either to the most recent or revert to an older version, ensuring missing migrations are run sequentially in the required order.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a very powerful feature offered by EF Core because it speeds up the development and evolution of the database schemas while ensuring a historic change log is kept and version controlled near the C# entity models. The application may have been developed for 10 years but any new team member can setup a local version of the database in just a few steps.&lt;/p&gt;
&lt;p&gt;But you are probably thinking: &lt;em&gt;this seems nice and all, but he’s talking about DevOps and I’m not sure how I can use this to evolve my production schemas? The company I work for does not allow any developer to connect to production databases and even if it did, certainly the user wouldn’t have permissions to change the database schema! I can’t run EF Core tools from my computer to do this!&lt;/em&gt;&lt;/p&gt;
&lt;h1 id=&#34;Why-an-installer-for-EF-Core-migrations&#34;&gt;&lt;a href=&#34;#Why-an-installer-for-EF-Core-migrations&#34; class=&#34;headerlink&#34; title=&#34;Why an installer for EF Core migrations?&#34;&gt;&lt;/a&gt;Why an installer for EF Core migrations?&lt;/h1&gt;&lt;p&gt;Having a console application that applies migrations to a database has some advantages that will probably benefit the DevOps process your company currently implements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For &lt;strong&gt;cloud solutions&lt;/strong&gt;, you can add steps into the Continuous Delivery pipeline that run the console before distributing the APIs or Websites, which may even require the approval of specific DevOps team members. Because the pipeline is tightly controlled, the production connection strings that can execute DDL instructions are properly secured. Even if you don’t have a DevOps team but the access to production is secured by the Infrastructure team, the console can be provided as a standalone executable and they decide how and where it should be run, manually or not.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;on-prem solutions&lt;/strong&gt;, the console can be packed with the application installer to be run every time a new version is released. If it fails, the installation process can either be halted or database changes reverted to a previous state by restoring a backup made at the start of the installation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In both scenarios this process is more reliable than, lets say, executing SQL scripts which are harder to read, maintain and debug in case of problems. If the console application has been configured with a good logging library (either to a file or command line output) there’s no reason someone can’t analyze what is happening.&lt;/p&gt;
&lt;p&gt;The use case scenario&lt;br&gt;To keep things simple and focusing in the migrations executable, let’s assume we are maintaining an API that manages a warehouse. This API is responsible for storing product information and stock movements and is implemented using a typical three-layer architecture, using Entity Framework Core to store and retrieve data from a SQL Server database.&lt;/p&gt;
&lt;p&gt;In this hypothetical scenario, we decided to improve our DevOps process by creating a console application to apply migrations into the database. This tool will be called migrator, which is a new console project inside the database layer.&lt;/p&gt;
&lt;p&gt;It will have a direct dependency from the database implementation, which holds the Entity Framework Core mappings for the database entities, defined in the database contracts layer.&lt;/p&gt;
&lt;p&gt;The diagram for projects and their dependencies inside the solution, after creating the migrator executable project, could look like this:&lt;/p&gt;
&lt;img src=&#34;/2023/12/04/NET-%E2%80%94-DevOps-and-Entity-Framework-Core/01_solution_diagram.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Since this API only manages products and stock movements, the database model could be represented by the following entity contracts (navigation properties won’t be used for simplicity):&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProductEntity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; Id &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Code &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Name &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;StockMovementEntity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; Id &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; ProductId &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; Quantity &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; DateTimeOffset OccurredOn &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The Entity Framework Core database context could be mapped as follows:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;        .HasMaxLength(&lt;span class=&#34;number&#34;&gt;8&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Name)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .HasMaxLength(&lt;span class=&#34;number&#34;&gt;128&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        modelBuilder.Entity&amp;lt;StockMovementEntity&amp;gt;(cfg =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.ToTable(&lt;span class=&#34;string&#34;&gt;&amp;quot;StockMovements&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasKey(e =&amp;gt; e.Id);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Id)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .ValueGeneratedOnAdd();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasOne&amp;lt;ProductEntity&amp;gt;()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .WithMany()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .HasForeignKey(e =&amp;gt; e.ProductId)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Quantity)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.OccurredOn)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As I said before, nothing fancy here, just a simple use-case with a database model we will use for creating our migrations and apply them using our console application.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/gravity00/article-devops-efcore&#34;&gt;I have a working example showcasing this scenario on GitHub (only the database layer), so feel free to give it a good look&lt;/a&gt;. I’m using .NET 8, but any previous .NET version will work without any code changes. If for some reason you need to support older framework versions like .NET Core App 2+ or .NET Framework 4.6.2+, just downgrade the nuggets to a previous versions that supports .NET Standard 2.0, like versions 2.x or 3.x, and do some small tweaks into the hosting setup.&lt;/p&gt;
&lt;h1 id=&#34;Console-application-setup&#34;&gt;&lt;a href=&#34;#Console-application-setup&#34; class=&#34;headerlink&#34; title=&#34;Console application setup&#34;&gt;&lt;/a&gt;Console application setup&lt;/h1&gt;&lt;p&gt;Now that we have defined both models and mappings, let’s setup the console application, which is going to use &lt;code&gt;Microsoft.Extensions.Hosting&lt;/code&gt; for an easier setup of logging, dependency injection and application settings configuration.&lt;/p&gt;
&lt;p&gt;Start by creating a console application project and create a &lt;code&gt;Program.cs&lt;/code&gt; file with a simple “hello world!” output.&lt;/p&gt;
&lt;p&gt;Add a project reference to the database implementation and add both &lt;code&gt;Microsoft.Extensions.Hosting&lt;/code&gt; and &lt;code&gt;Microsoft.EntityFrameworkCore.Tools&lt;/code&gt; packages (we are going to use Visual Studio Package Manager Console for creating the migrations, but feel free to use .NET Core CLI tools).&lt;/p&gt;
&lt;p&gt;Inside the database layer, the projects should be similar to this:&lt;/p&gt;
&lt;img src=&#34;/2023/12/04/NET-%E2%80%94-DevOps-and-Entity-Framework-Core/02_project_structure.png&#34; class=&#34;&#34;&gt;

&lt;h2 id=&#34;Migrations-setup&#34;&gt;&lt;a href=&#34;#Migrations-setup&#34; class=&#34;headerlink&#34; title=&#34;Migrations setup&#34;&gt;&lt;/a&gt;Migrations setup&lt;/h2&gt;&lt;p&gt;To prepare the project for creating EF Core migrations, start by adding an &lt;code&gt;appsettings.json&lt;/code&gt; file with a connection string to an empty SQL Server database and, to be used in the future by the host, you can already make some logging configurations.&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;ConnectionStrings&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;ArticleDevOpsEfCore&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;Data Source=localhost;Database=ArticleDevOpsEfCore;User Id=sa;Password=abcd1234;Encrypt=false;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;Logging&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;LogLevel&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;span class=&#34;attr&#34;&gt;&amp;quot;Default&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;Trace&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;span class=&#34;attr&#34;&gt;&amp;quot;Microsoft&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;Information&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;Console&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;span class=&#34;attr&#34;&gt;&amp;quot;IncludeScopes&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;Debug&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;span class=&#34;attr&#34;&gt;&amp;quot;IncludeScopes&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;EventSource&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;span class=&#34;attr&#34;&gt;&amp;quot;IncludeScopes&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Remember to tell the application that, when publishing, the configuration files must be copied to the output folder. This is done by adding an include property to the &lt;em&gt;csproj&lt;/em&gt; file.&lt;/p&gt;
&lt;figure class=&#34;highlight xml&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;ItemGroup&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;None&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;Include&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;*.config;*.json&amp;quot;&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;CopyToOutputDirectory&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;PreserveNewest&amp;quot;&lt;/span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;ItemGroup&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Create a new class implementing &lt;code&gt;IDesignTimeDbContextFactory&lt;/code&gt;. This class will be responsible for creating and configuring the database contexts when managing migrations via .NET Core CLI tools or Visual Studio Package Manager Console.&lt;/p&gt;
&lt;p&gt;To know which connection string must be used, we are going to use a &lt;code&gt;ConfigurationBuilder&lt;/code&gt; to read the settings file and then register a SQL Server provider with a small detail — &lt;strong&gt;we must specify that the migrations are stored in the console application executable, not where the context is defined&lt;/strong&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;DesignTimeDbContextFactory&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IDesignTimeDbContextFactory&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;DatabaseContext&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; DatabaseContext &lt;span class=&#34;title&#34;&gt;CreateDbContext&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;[] args&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; configuration = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ConfigurationBuilder()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .SetBasePath(Directory.GetCurrentDirectory())&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .AddJsonFile(&lt;span class=&#34;string&#34;&gt;&amp;quot;appsettings.json&amp;quot;&lt;/span&gt;, &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .Build();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; options = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; DbContextOptionsBuilder&amp;lt;DatabaseContext&amp;gt;().UseSqlServer(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            configuration.GetConnectionString(&lt;span class=&#34;string&#34;&gt;&amp;quot;ArticleDevOpsEfCore&amp;quot;&lt;/span&gt;),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o =&amp;gt; o.MigrationsAssembly(&lt;span class=&#34;keyword&#34;&gt;typeof&lt;/span&gt;(Program).Assembly.FullName)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ).Options;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; DatabaseContext(options);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Let’s test the setup and see if we can create some migrations. Open the Package Manager Console and run the following command:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;add-migration InitialModelSetup&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If everything went as expected, a new &lt;code&gt;Migrations&lt;/code&gt; folder was created with a &lt;code&gt;InitialModelSetup&lt;/code&gt; migration class that will setup the database model. If your database schema is empty, it should look like this:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; &lt;span class=&#34;doctag&#34;&gt;&amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;partial&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;InitialModelSetup&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;Migration&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; &lt;span class=&#34;doctag&#34;&gt;&amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Up&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;MigrationBuilder migrationBuilder&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        migrationBuilder.CreateTable(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            name: &lt;span class=&#34;string&#34;&gt;&amp;quot;Products&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            columns: table =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Id = table.Column&amp;lt;&lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt;&amp;gt;(type: &lt;span class=&#34;string&#34;&gt;&amp;quot;bigint&amp;quot;&lt;/span&gt;, nullable: &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    .Annotation(&lt;span class=&#34;string&#34;&gt;&amp;quot;SqlServer:Identity&amp;quot;&lt;/span&gt;, &lt;span class=&#34;string&#34;&gt;&amp;quot;1, 1&amp;quot;&lt;/span&gt;),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Code = table.Column&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt;(type: &lt;span class=&#34;string&#34;&gt;&amp;quot;nvarchar(8)&amp;quot;&lt;/span&gt;, maxLength: &lt;span class=&#34;number&#34;&gt;8&lt;/span&gt;, nullable: &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Name = table.Column&amp;lt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;&amp;gt;(type: &lt;span class=&#34;string&#34;&gt;&amp;quot;nvarchar(128)&amp;quot;&lt;/span&gt;, maxLength: &lt;span class=&#34;number&#34;&gt;128&lt;/span&gt;, nullable: &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            constraints: table =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                table.PrimaryKey(&lt;span class=&#34;string&#34;&gt;&amp;quot;PK_Products&amp;quot;&lt;/span&gt;, x =&amp;gt; x.Id);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        migrationBuilder.CreateTable(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            name: &lt;span class=&#34;string&#34;&gt;&amp;quot;StockMovements&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            columns: table =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Id = table.Column&amp;lt;&lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt;&amp;gt;(type: &lt;span class=&#34;string&#34;&gt;&amp;quot;bigint&amp;quot;&lt;/span&gt;, nullable: &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    .Annotation(&lt;span class=&#34;string&#34;&gt;&amp;quot;SqlServer:Identity&amp;quot;&lt;/span&gt;, &lt;span class=&#34;string&#34;&gt;&amp;quot;1, 1&amp;quot;&lt;/span&gt;),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                ProductId = table.Column&amp;lt;&lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt;&amp;gt;(type: &lt;span class=&#34;string&#34;&gt;&amp;quot;bigint&amp;quot;&lt;/span&gt;, nullable: &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Quantity = table.Column&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt;(type: &lt;span class=&#34;string&#34;&gt;&amp;quot;int&amp;quot;&lt;/span&gt;, nullable: &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                OccurredOn = table.Column&amp;lt;DateTimeOffset&amp;gt;(type: &lt;span class=&#34;string&#34;&gt;&amp;quot;datetimeoffset&amp;quot;&lt;/span&gt;, nullable: &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            constraints: table =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                table.PrimaryKey(&lt;span class=&#34;string&#34;&gt;&amp;quot;PK_StockMovements&amp;quot;&lt;/span&gt;, x =&amp;gt; x.Id);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                table.ForeignKey(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    name: &lt;span class=&#34;string&#34;&gt;&amp;quot;FK_StockMovements_Products_ProductId&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    column: x =&amp;gt; x.ProductId,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    principalTable: &lt;span class=&#34;string&#34;&gt;&amp;quot;Products&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    principalColumn: &lt;span class=&#34;string&#34;&gt;&amp;quot;Id&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    onDelete: ReferentialAction.Cascade);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        migrationBuilder.CreateIndex(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            name: &lt;span class=&#34;string&#34;&gt;&amp;quot;IX_Products_Code&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            table: &lt;span class=&#34;string&#34;&gt;&amp;quot;Products&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            column: &lt;span class=&#34;string&#34;&gt;&amp;quot;Code&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            unique: &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        migrationBuilder.CreateIndex(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            name: &lt;span class=&#34;string&#34;&gt;&amp;quot;IX_StockMovements_ProductId&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            table: &lt;span class=&#34;string&#34;&gt;&amp;quot;StockMovements&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            column: &lt;span class=&#34;string&#34;&gt;&amp;quot;ProductId&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; &lt;span class=&#34;doctag&#34;&gt;&amp;lt;inheritdoc /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Down&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;MigrationBuilder migrationBuilder&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        migrationBuilder.DropTable(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            name: &lt;span class=&#34;string&#34;&gt;&amp;quot;StockMovements&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        migrationBuilder.DropTable(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            name: &lt;span class=&#34;string&#34;&gt;&amp;quot;Products&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Hosting-setup&#34;&gt;&lt;a href=&#34;#Hosting-setup&#34; class=&#34;headerlink&#34; title=&#34;Hosting setup&#34;&gt;&lt;/a&gt;Hosting setup&lt;/h2&gt;&lt;p&gt;Now that we know the project is ready to add or remove migrations based on our development database, let’s create all the required code to apply migrations when running the console.&lt;/p&gt;
&lt;p&gt;Open the launch profile, and in the environment variables, add a new &lt;code&gt;DOTNET_ENVIRONMENT&lt;/code&gt; with the value &lt;code&gt;development&lt;/code&gt;. This will be used by the host to know your current environment — development, stagging or production (default) — when running the application which is useful for loading different settings or executing code based on the environment, like creating test data.&lt;/p&gt;
&lt;img src=&#34;/2023/12/04/NET-%E2%80%94-DevOps-and-Entity-Framework-Core/03_environment_setup.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Open the &lt;code&gt;Program.cs&lt;/code&gt; file and use &lt;code&gt;Host.CreateApplicationBuilder&lt;/code&gt; to create a builder for setting up a new host. It will use pre-configured defaults, like loading settings from the &lt;code&gt;appsettings.json&lt;/code&gt; file or from environment variables and output logging to console and debug windows, while supporting dependency injection.&lt;/p&gt;
&lt;p&gt;You can use the builder to register other logging providers, like NLog or Serilog, and register the Entity Framework Core context into the dependency injection container using a SQL Server provider. Because the migrations are going to be run by a console application, the context can be shared as a singleton (by default it is scoped, useful for ASP.NET Core apps), otherwise you must create a scope explicitly. Once again, don’t forget to specify the assembly were migrations are stored.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; builder = Host.CreateApplicationBuilder(args);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ConfigureLogging(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    builder.Logging&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ConfigureServices(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    builder.Services,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    builder.Configuration&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; host = builder.Build();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ConfigureLogging&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    ILoggingBuilder logging&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// configure other logging providers, like NLog, Serilog or even Application Insights&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ConfigureServices&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    IServiceCollection services,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    IConfiguration configuration&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; connectionString = configuration.GetConnectionString(&lt;span class=&#34;string&#34;&gt;&amp;quot;ArticleDevOpsEfCore&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    services.AddDbContext&amp;lt;DatabaseContext&amp;gt;(options =&amp;gt; options.UseSqlServer(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        connectionString,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        o =&amp;gt; o.MigrationsAssembly(&lt;span class=&#34;keyword&#34;&gt;typeof&lt;/span&gt;(Program).Assembly.FullName)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ), ServiceLifetime.Singleton);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Create a new class named &lt;code&gt;Migrator&lt;/code&gt; with a &lt;code&gt;RunAsync&lt;/code&gt; method (that will have the migrator implementation), register it as a singleton into the DI container and, after creating the host, resolve and run it. Because migrations can fail, it is also a good idea to implement some exception handling logic (because even host setup can fail, I also prefer to wrap it). Also, we should support the cancellation of this process when CTRL+C or exit is requested, so let’s use a &lt;code&gt;CancellationTokenSource&lt;/code&gt; for that.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; cts = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CancellationTokenSource();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;Console.CancelKeyPress += (_, eventArgs) =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    cts.Cancel();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    eventArgs.Cancel = &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ILogger&amp;lt;Program&amp;gt; logger = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; builder = Host.CreateApplicationBuilder(args);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ConfigureLogging(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        builder.Logging&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ConfigureServices(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        builder.Services,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        builder.Configuration&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; host = builder.Build();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    logger = host.Services.GetRequiredService&amp;lt;ILogger&amp;lt;Program&amp;gt;&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; migrator = host.Services.GetRequiredService&amp;lt;Migrator&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Running migrator&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; migrator.RunAsync(cts.Token);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    logger.LogInformation(&lt;span class=&#34;string&#34;&gt;&amp;quot;Migrator run successfully&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (Exception e)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (logger &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; Console.Error.WriteLineAsync(&lt;span class=&#34;string&#34;&gt;&amp;quot;Application failed with a fatal error&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; Console.Error.WriteLineAsync(e.ToString());&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;else&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        logger.LogCritical(e, &lt;span class=&#34;string&#34;&gt;&amp;quot;Application failed with a fatal error&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ConfigureLogging&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    ILoggingBuilder logging&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// configure other logging providers, like NLog, Serilog or even Application Insights&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ConfigureServices&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    IServiceCollection services,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    IConfiguration configuration&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; connectionString = configuration.GetConnectionString(&lt;span class=&#34;string&#34;&gt;&amp;quot;ArticleDevOpsEfCore&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    services.AddDbContext&amp;lt;DatabaseContext&amp;gt;(options =&amp;gt; options.UseSqlServer(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        connectionString,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        o =&amp;gt; o.MigrationsAssembly(&lt;span class=&#34;keyword&#34;&gt;typeof&lt;/span&gt;(Program).Assembly.FullName)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ), ServiceLifetime.Singleton);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    services.AddSingleton&amp;lt;Migrator&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Applying-migrations&#34;&gt;&lt;a href=&#34;#Applying-migrations&#34; class=&#34;headerlink&#34; title=&#34;Applying migrations&#34;&gt;&lt;/a&gt;Applying migrations&lt;/h2&gt;&lt;p&gt;With the host fully configured and ready to run the migrator, let’s implement the migrations logic.&lt;/p&gt;
&lt;p&gt;Open the &lt;code&gt;Migrator.cs&lt;/code&gt; file and create a constructor receiving both a logger and a database context. Inside the &lt;code&gt;RunAsync&lt;/code&gt; method add some helpful logging and use the &lt;code&gt;ctx.Database.MigrateAsync&lt;/code&gt; to update the database schema to the latest available. Keep in mind this method will apply the missing migrations and it’s not the same as &lt;code&gt;EnsureCreatedAsync&lt;/code&gt;, which just checks if the database exists and if it doesn’t, creates the database and schema without using any migration.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Migrator&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    ILogger&amp;lt;Migrator&amp;gt; _logger,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    DatabaseContext _context&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; ValueTask &lt;span class=&#34;title&#34;&gt;RunAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Migrating database to the latest version&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.MigrateAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogInformation(&lt;span class=&#34;string&#34;&gt;&amp;quot;Database migrated to latest version&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you now run the console, you’ll see some output showing the DDL instructions that are being run and if everything went as expected the database should now list both the EF Core migration history (used to track which migrations are applied) and business tables.&lt;/p&gt;
&lt;img src=&#34;/2023/12/04/NET-%E2%80%94-DevOps-and-Entity-Framework-Core/04_db_tables.png&#34; class=&#34;&#34;&gt;

&lt;h2 id=&#34;Seeding-data&#34;&gt;&lt;a href=&#34;#Seeding-data&#34; class=&#34;headerlink&#34; title=&#34;Seeding data&#34;&gt;&lt;/a&gt;Seeding data&lt;/h2&gt;&lt;p&gt;Despite Entity Framework Core supporting data seeds when applying migrations, I think it is limited for production scenarios, like seeding different data based on environment or need to run some business logic, you will face limitations if using what EF Core offers.&lt;/p&gt;
&lt;p&gt;To solve this problem I usually create a specialized interface for seeding data into the migrator project, so developers may implement how many seed logic they need and even decide the order by which they are run.&lt;/p&gt;
&lt;p&gt;In the migrator project, create a new &lt;code&gt;IDataSeed&lt;/code&gt; interface, inside a &lt;code&gt;DataSeeds&lt;/code&gt; folder, with a &lt;code&gt;SeedAsync&lt;/code&gt; method. As a small note, I like to pass arguments that can be used for auditing so I can use them on metadata columns, but they aren’t a requirement — just pass what makes sense to you.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;IDataSeed&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;ValueTask &lt;span class=&#34;title&#34;&gt;SeedAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; seededBy,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        DateTimeOffset seededOn,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Let’s change the migrator to run all registered seeds after applying the migrations. Open the &lt;code&gt;Migrator.cs&lt;/code&gt;, pass a collection of data seeds to the constructor and then, inside a transaction, execute each seed one by one. I recommend to flush changes after each seed to be sure they all see the data created by each other, even if some have to execute native SQL code for performance reasons.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Migrator&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    ILogger&amp;lt;Migrator&amp;gt; _logger,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    DatabaseContext _context,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    IEnumerable&amp;lt;IDataSeed&amp;gt; _dataSeeds&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; ValueTask &lt;span class=&#34;title&#34;&gt;RunAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Migrating database to the latest version&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.MigrateAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogInformation(&lt;span class=&#34;string&#34;&gt;&amp;quot;Database migrated to latest version&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; seededOn = DateTimeOffset.UtcNow;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; tx = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.BeginTransactionAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; dataSeed &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; _dataSeeds)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; _ = _logger.BeginScope(&lt;span class=&#34;string&#34;&gt;&amp;quot;DataSeed:&amp;#123;DataSeed&amp;#125;&amp;quot;&lt;/span&gt;, dataSeed.GetType().Name);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Seeding data&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; dataSeed.SeedAsync(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;string&#34;&gt;&amp;quot;Migrator&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                seededOn,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                ct&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.SaveChangesAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _logger.LogInformation(&lt;span class=&#34;string&#34;&gt;&amp;quot;Data was seeded&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; tx.CommitAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;To test the implemented data seed logic, let’s assume we wanted to create test products for development and staging environments, but not in production. Create a new &lt;code&gt;TestProductsDataSeed&lt;/code&gt; inside the &lt;code&gt;DataSeeds&lt;/code&gt; folder and implement the &lt;code&gt;SeedAsync&lt;/code&gt; method creating a bunch of products if they don’t exist and the migrator isn’t running in production.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;TestProductsDataSeed&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    ILogger&amp;lt;TestProductsDataSeed&amp;gt; _logger,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    IHostEnvironment _env,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    DatabaseContext _context&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;) : IDataSeed&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; ValueTask &lt;span class=&#34;title&#34;&gt;SeedAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; seededBy, DateTimeOffset seededOn, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_env.IsProduction())&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _logger.LogInformation(&lt;span class=&#34;string&#34;&gt;&amp;quot;Running in production, no test data will be seeded&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; productsSet = _context.Set&amp;lt;ProductEntity&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; testCodes = Enumerable.Range(&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;150&lt;/span&gt;).Select(i =&amp;gt; i.ToString(&lt;span class=&#34;string&#34;&gt;&amp;quot;D8&amp;quot;&lt;/span&gt;, NumberFormatInfo.InvariantInfo)).ToArray();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; existingCodes = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; (&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;from&lt;/span&gt; p &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; productsSet&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                testCodes.Contains(p.Code)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;select&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                p.Code&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ).ToArrayAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; nonExistingProducts = testCodes.Where(c =&amp;gt; !existingCodes.Contains(c)).Select(c =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ProductEntity&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Code = c,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Name = &lt;span class=&#34;string&#34;&gt;$&amp;quot;Test product &amp;#x27;&lt;span class=&#34;subst&#34;&gt;&amp;#123;c&amp;#125;&lt;/span&gt;&amp;#x27;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; productsSet.AddRangeAsync(nonExistingProducts, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now open the &lt;code&gt;Program.cs&lt;/code&gt; file and register the &lt;code&gt;TestProductsDataSeed&lt;/code&gt; as a singleton. As a note, you can scan the assembly for classes implementing &lt;code&gt;IDataSeed&lt;/code&gt; (with &lt;a href=&#34;https://github.com/khellang/Scrutor&#34;&gt;Scrutor&lt;/a&gt;, for example) but the order or registration will define the order by which they are run, and I’m assuming that will matter, but feel free to use an approach that works for your scenario.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ConfigureServices&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    IServiceCollection services,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    IConfiguration configuration&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; connectionString = configuration.GetConnectionString(&lt;span class=&#34;string&#34;&gt;&amp;quot;ArticleDevOpsEfCore&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    services.AddDbContext&amp;lt;DatabaseContext&amp;gt;(options =&amp;gt; options.UseSqlServer(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        connectionString,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        o =&amp;gt; o.MigrationsAssembly(&lt;span class=&#34;keyword&#34;&gt;typeof&lt;/span&gt;(Program).Assembly.FullName)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ), ServiceLifetime.Singleton);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    services.AddSingleton&amp;lt;Migrator&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    services.AddSingleton&amp;lt;IDataSeed, TestProductsDataSeed&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If we now run the migrator the products table should have test data.&lt;/p&gt;
&lt;img src=&#34;/2023/12/04/NET-%E2%80%94-DevOps-and-Entity-Framework-Core/05_db_seeded.png&#34; class=&#34;&#34;&gt;

&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;In this article I explained how DevOps processes can be simplified by implementing a console application that knows how to apply Entity Framework Core migrations and seed initial data to a database, to be introduced into de Continuous Delivery pipeline.&lt;/p&gt;
&lt;p&gt;The implementation using &lt;code&gt;Microsoft.Extensions.Hosting&lt;/code&gt; is very straightforward and simple while providing a more tight integration against the model definitions and with improved logging, so it certainly is an approach to consider.&lt;/p&gt;
&lt;p&gt;Even with small tweaks, in can be improved to whatever scenarios you may face, like forcing a backup before applying the migrations and reverting to it if something fails, perfect for on-prem solutions.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/gravity00/article-devops-efcore&#34;&gt;Once again, I have a working example on GitHub, so feel free to give it a good look.&lt;/a&gt;&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="csharp" />
        <category term="efcore" />
        <updated>2023-12-04T00:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2023/11/09/NET-%E2%80%94-ToList-vs-ToArray/</id>
        <title>.NET — ToList vs ToArray</title>
        <link rel="alternate" href="https://code-corner.dev/2023/11/09/NET-%E2%80%94-ToList-vs-ToArray/"/>
        <content type="html">&lt;p&gt;Ever since Microsoft introduced Language Integrated Query to the .NET framework (also known as LINQ) developers have been using it extensively to work with collections.&lt;/p&gt;
&lt;p&gt;From a simple filter, to an aggregation, to a transformation, LINQ is the technology of choice to keep code clean and readable. We even have providers that convert LINQ instructions into SQL commands that will be run in some database.&lt;/p&gt;
&lt;p&gt;In this article I’m going to compare the performance of &lt;code&gt;ToList&lt;/code&gt; versus &lt;code&gt;ToArray&lt;/code&gt; when creating short lived collections. I’m also going to execute the test in different versions of the framework (.NET Framework 4.8, .NET 7 and .NET 8) so we can also see how much the performance have improved over the years.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;One of the coding guidelines for the applications I manage dictates the following:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Always use &lt;code&gt;IReadOnlyCollection&lt;/code&gt; instead of &lt;code&gt;IEnumerable&lt;/code&gt; when passing collections between application layers and &lt;code&gt;ToArray&lt;/code&gt; should be used to force the enumeration.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Because we developers are curious by default and need to understand why things are implemented in a given way, every time we have a new team member, that coding guideline usually leads to the following conversation:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q:&lt;/strong&gt; &lt;em&gt;Why do we use &lt;code&gt;IReadOnlyCollection&lt;/code&gt; in POCOs instead of &lt;code&gt;IEnumerable&lt;/code&gt;?&lt;/em&gt;&lt;br&gt;&lt;strong&gt;A:&lt;/strong&gt; &lt;em&gt;Well, because we want the contracts to clearly state the collection is in memory, hence no multiple enumerations will occur, and any mapping problems will happen in the corresponding layer.&lt;/em&gt;&lt;br&gt;&lt;strong&gt;Q:&lt;/strong&gt; &lt;em&gt;Fair enough, but why &lt;code&gt;ToArray&lt;/code&gt;? That interface is implemented by arrays and lists, I could be using &lt;code&gt;ToList&lt;/code&gt; and have the same result.&lt;/em&gt;&lt;br&gt;&lt;strong&gt;A:&lt;/strong&gt; &lt;em&gt;The result is the same, that’s a fact, but &lt;code&gt;ToArray&lt;/code&gt; is usually faster and more memory efficient than &lt;code&gt;ToList&lt;/code&gt;, and since it’s a short lived collection that won’t be mutated, the former is preferred.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I’m going to use the well known C# library &lt;code&gt;BenchmarkDotNet&lt;/code&gt; to run the tests and the environment will be the following:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;BenchmarkDotNet v0.13.10, Windows 11 (10.0.22621.2428/22H2/2022Update/SunValley2)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;AMD Ryzen 7 3700X, 1 CPU, 16 logical and 8 physical cores&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;.NET SDK 8.0.100-rc.2.23502.2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  [Host]             : .NET 8.0.0 (8.0.23.47906), X64 RyuJIT AVX2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  .NET 5.0           : .NET 5.0.17 (5.0.1722.21314), X64 RyuJIT AVX2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  .NET 7.0           : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  .NET 8.0           : .NET 8.0.0 (8.0.23.47906), X64 RyuJIT AVX2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  .NET Framework 4.8 : .NET Framework 4.8.1 (4.8.9181.0), X64 RyuJIT VectorSize=256&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Performance-test&#34;&gt;&lt;a href=&#34;#Performance-test&#34; class=&#34;headerlink&#34; title=&#34;Performance test&#34;&gt;&lt;/a&gt;Performance test&lt;/h1&gt;&lt;p&gt;The test consists in the creation of a collection that holds random integers, being the size defined by a parameter. To ensure the randomness does not affect the results, the values are cached into an array and before invoking either &lt;code&gt;ToArray&lt;/code&gt; or &lt;code&gt;ToList&lt;/code&gt; it is converted into a new &lt;code&gt;IEnumerable&lt;/code&gt;, not just a cast that could lead to internal optimizations.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;SimpleJob(RuntimeMoniker.Net48)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;SimpleJob(RuntimeMoniker.Net70)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;SimpleJob(RuntimeMoniker.Net80)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;MemoryDiagnoser&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ToListVsToArray&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Params(10, 100, 1000, 10000, 100000)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; Size;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;[] _items;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;GlobalSetup&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Setup&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; random = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Random(&lt;span class=&#34;number&#34;&gt;123&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _items = Enumerable.Range(&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;, Size).Select(_ =&amp;gt; random.Next()).ToArray();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;[] &lt;span class=&#34;title&#34;&gt;ToArray&lt;/span&gt;()&lt;/span&gt; =&amp;gt; CreateItemsEnumerable().ToArray();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; List&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;ToList&lt;/span&gt;()&lt;/span&gt; =&amp;gt; CreateItemsEnumerable().ToList();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; IEnumerable&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;CreateItemsEnumerable&lt;/span&gt;()&lt;/span&gt; =&amp;gt; _items.Select(e =&amp;gt; e);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Performance-results&#34;&gt;&lt;a href=&#34;#Performance-results&#34; class=&#34;headerlink&#34; title=&#34;Performance results&#34;&gt;&lt;/a&gt;Performance results&lt;/h1&gt;&lt;p&gt;Because we want to decide, for a given application, between ToArray or ToList based on performance, let’s first analyze the results for each framework version.&lt;/p&gt;
&lt;h2 id=&#34;NET-Framework-4-8&#34;&gt;&lt;a href=&#34;#NET-Framework-4-8&#34; class=&#34;headerlink&#34; title=&#34;.NET Framework 4.8&#34;&gt;&lt;/a&gt;.NET Framework 4.8&lt;/h2&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Method  | Size   | Mean          | Error        | StdDev       | Gen0     | Gen1     | Gen2     | Allocated |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|-------- |------- |--------------:|-------------:|-------------:|---------:|---------:|---------:|----------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 10     |     166.31 ns |     1.013 ns |     0.947 ns |   0.0484 |        - |        - |     305 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 10     |     193.17 ns |     1.248 ns |     1.168 ns |   0.0446 |        - |        - |     281 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 100    |     965.26 ns |     8.436 ns |     7.479 ns |   0.2594 |        - |        - |    1637 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 100    |   1,032.99 ns |     4.144 ns |     3.876 ns |   0.1984 |        - |        - |    1252 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 1000   |   8,377.61 ns |    39.382 ns |    36.838 ns |   1.9836 |        - |        - |   12509 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 1000   |   8,665.62 ns |    57.777 ns |    54.045 ns |   1.3428 |   0.0153 |        - |    8514 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 10000  |  81,576.33 ns |   923.478 ns |   863.822 ns |  26.9775 |   5.3711 |        - |  171755 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 10000  |  83,476.64 ns |   410.694 ns |   342.948 ns |  20.7520 |   4.0283 |        - |  131606 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 100000 | 830,624.20 ns | 4,536.991 ns | 4,021.924 ns | 399.4141 | 399.4141 | 399.4141 | 1452144 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 100000 | 945,017.84 ns | 7,921.731 ns | 6,615.004 ns | 285.1563 | 285.1563 | 285.1563 | 1051184 B |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code&gt;ToArray&lt;/code&gt; method is, on average, 10% faster than &lt;code&gt;ToList&lt;/code&gt; in .NET Framework 4.8.&lt;/p&gt;
&lt;h2 id=&#34;NET-7&#34;&gt;&lt;a href=&#34;#NET-7&#34; class=&#34;headerlink&#34; title=&#34;.NET 7&#34;&gt;&lt;/a&gt;.NET 7&lt;/h2&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Method  | Size   | Mean          | Error        | StdDev       | Gen0     | Gen1     | Gen2     | Allocated |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|-------- |------- |--------------:|-------------:|-------------:|---------:|---------:|---------:|----------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 10     |      50.06 ns |     0.916 ns |     1.254 ns |   0.0134 |        - |        - |     112 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 10     |      56.20 ns |     1.022 ns |     0.906 ns |   0.0172 |        - |        - |     144 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 100    |     231.18 ns |     1.597 ns |     1.494 ns |   0.0563 |        - |        - |     472 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 100    |     261.38 ns |     2.523 ns |     2.236 ns |   0.0601 |        - |        - |     504 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 1000   |   2,029.47 ns |    28.534 ns |    25.295 ns |   0.4845 |        - |        - |    4072 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 1000   |   2,291.63 ns |    13.328 ns |    11.815 ns |   0.4883 |        - |        - |    4104 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 10000  |  17,322.99 ns |   176.548 ns |   165.143 ns |   4.7607 |        - |        - |   40072 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 10000  |  22,781.69 ns |   200.720 ns |   177.933 ns |   4.7607 |        - |        - |   40104 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 100000 | 306,976.29 ns | 2,525.016 ns | 2,361.901 ns | 124.5117 | 124.5117 | 124.5117 |  400114 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 100000 | 337,437.79 ns | 2,441.397 ns | 2,283.684 ns | 124.5117 | 124.5117 | 124.5117 |  400146 B |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code&gt;ToArray&lt;/code&gt; method is, on average, 13% faster than &lt;code&gt;ToList&lt;/code&gt; in .NET 7.&lt;/p&gt;
&lt;h2 id=&#34;NET-8&#34;&gt;&lt;a href=&#34;#NET-8&#34; class=&#34;headerlink&#34; title=&#34;.NET 8&#34;&gt;&lt;/a&gt;.NET 8&lt;/h2&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Method  | Size   | Mean          | Error        | StdDev       | Gen0     | Gen1     | Gen2     | Allocated |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|-------- |------- |--------------:|-------------:|-------------:|---------:|---------:|---------:|----------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 10     |      33.89 ns |     0.727 ns |     0.778 ns |   0.0134 |        - |        - |     112 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 10     |      40.17 ns |     0.668 ns |     0.625 ns |   0.0172 |        - |        - |     144 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 100    |      90.30 ns |     1.104 ns |     0.922 ns |   0.0564 |        - |        - |     472 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 100    |      97.87 ns |     1.257 ns |     1.176 ns |   0.0602 |        - |        - |     504 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 1000   |     615.97 ns |     6.819 ns |     5.695 ns |   0.4864 |        - |        - |    4072 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 1000   |     615.44 ns |     5.195 ns |     4.056 ns |   0.4902 |        - |        - |    4104 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 10000  |   5,335.01 ns |    86.179 ns |    76.395 ns |   4.7607 |        - |        - |   40072 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 10000  |   5,427.51 ns |    86.063 ns |    80.503 ns |   4.7836 |        - |        - |   40104 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToArray | 100000 | 169,711.96 ns | 2,405.080 ns | 2,249.713 ns | 124.7559 | 124.7559 | 124.7559 |  400114 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ToList  | 100000 | 162,577.72 ns | 1,634.437 ns | 1,364.829 ns | 124.7559 | 124.7559 | 124.7559 |  400146 B |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code&gt;ToArray&lt;/code&gt; method is, by a small margin, faster than &lt;code&gt;ToList&lt;/code&gt; in .NET 8, being 4% slower on larger collections.&lt;/p&gt;
&lt;h1 id=&#34;NET-performance-evolution&#34;&gt;&lt;a href=&#34;#NET-performance-evolution&#34; class=&#34;headerlink&#34; title=&#34;.NET performance evolution&#34;&gt;&lt;/a&gt;.NET performance evolution&lt;/h1&gt;&lt;p&gt;Because we have results for each framework version, let’s compare and see how .NET performance has evolved over the years.&lt;/p&gt;
&lt;h2 id=&#34;ToArray&#34;&gt;&lt;a href=&#34;#ToArray&#34; class=&#34;headerlink&#34; title=&#34;ToArray&#34;&gt;&lt;/a&gt;ToArray&lt;/h2&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Runtime            | Size   | Mean          | Error        | StdDev       | Gen0     | Gen1     | Gen2     | Allocated |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|------------------- |------- |--------------:|-------------:|-------------:|---------:|---------:|---------:|----------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8 | 10     |     166.31 ns |     1.013 ns |     0.947 ns |   0.0484 |        - |        - |     305 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 7.0           | 10     |      50.06 ns |     0.916 ns |     1.254 ns |   0.0134 |        - |        - |     112 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0           | 10     |      33.89 ns |     0.727 ns |     0.778 ns |   0.0134 |        - |        - |     112 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8 | 100    |     965.26 ns |     8.436 ns |     7.479 ns |   0.2594 |        - |        - |    1637 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 7.0           | 100    |     231.18 ns |     1.597 ns |     1.494 ns |   0.0563 |        - |        - |     472 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0           | 100    |      90.30 ns |     1.104 ns |     0.922 ns |   0.0564 |        - |        - |     472 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8 | 1000   |   8,377.61 ns |    39.382 ns |    36.838 ns |   1.9836 |        - |        - |   12509 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 7.0           | 1000   |   2,029.47 ns |    28.534 ns |    25.295 ns |   0.4845 |        - |        - |    4072 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0           | 1000   |     615.97 ns |     6.819 ns |     5.695 ns |   0.4864 |        - |        - |    4072 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8 | 10000  |  81,576.33 ns |   923.478 ns |   863.822 ns |  26.9775 |   5.3711 |        - |  171755 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 7.0           | 10000  |  17,322.99 ns |   176.548 ns |   165.143 ns |   4.7607 |        - |        - |   40072 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0           | 10000  |   5,335.01 ns |    86.179 ns |    76.395 ns |   4.7607 |        - |        - |   40072 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8 | 100000 | 830,624.20 ns | 4,536.991 ns | 4,021.924 ns | 399.4141 | 399.4141 | 399.4141 | 1452144 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 7.0           | 100000 | 306,976.29 ns | 2,525.016 ns | 2,361.901 ns | 124.5117 | 124.5117 | 124.5117 |  400114 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0           | 100000 | 169,711.96 ns | 2,405.080 ns | 2,249.713 ns | 124.7559 | 124.7559 | 124.7559 |  400114 B |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;In some cases being 90% faster than .NET Framework 4.8 and more than 50% faster than .NET 7 while allocating less memory, .NET 8 is a clear winner.&lt;/p&gt;
&lt;h2 id=&#34;ToList&#34;&gt;&lt;a href=&#34;#ToList&#34; class=&#34;headerlink&#34; title=&#34;ToList&#34;&gt;&lt;/a&gt;ToList&lt;/h2&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Runtime            | Size   | Mean          | Error        | StdDev       | Gen0     | Gen1     | Gen2     | Allocated |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|------------------- |------- |--------------:|-------------:|-------------:|---------:|---------:|---------:|----------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8 | 10     |     193.17 ns |     1.248 ns |     1.168 ns |   0.0446 |        - |        - |     281 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 7.0           | 10     |      56.20 ns |     1.022 ns |     0.906 ns |   0.0172 |        - |        - |     144 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0           | 10     |      40.17 ns |     0.668 ns |     0.625 ns |   0.0172 |        - |        - |     144 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8 | 100    |   1,032.99 ns |     4.144 ns |     3.876 ns |   0.1984 |        - |        - |    1252 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 7.0           | 100    |     261.38 ns |     2.523 ns |     2.236 ns |   0.0601 |        - |        - |     504 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0           | 100    |      97.87 ns |     1.257 ns |     1.176 ns |   0.0602 |        - |        - |     504 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8 | 1000   |   8,665.62 ns |    57.777 ns |    54.045 ns |   1.3428 |   0.0153 |        - |    8514 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 7.0           | 1000   |   2,291.63 ns |    13.328 ns |    11.815 ns |   0.4883 |        - |        - |    4104 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0           | 1000   |     615.44 ns |     5.195 ns |     4.056 ns |   0.4902 |        - |        - |    4104 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8 | 10000  |  83,476.64 ns |   410.694 ns |   342.948 ns |  20.7520 |   4.0283 |        - |  131606 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 7.0           | 10000  |  22,781.69 ns |   200.720 ns |   177.933 ns |   4.7607 |        - |        - |   40104 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0           | 10000  |   5,427.51 ns |    86.063 ns |    80.503 ns |   4.7836 |        - |        - |   40104 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET Framework 4.8 | 100000 | 945,017.84 ns | 7,921.731 ns | 6,615.004 ns | 285.1563 | 285.1563 | 285.1563 | 1051184 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 7.0           | 100000 | 337,437.79 ns | 2,441.397 ns | 2,283.684 ns | 124.5117 | 124.5117 | 124.5117 |  400146 B |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| .NET 8.0           | 100000 | 162,577.72 ns | 1,634.437 ns | 1,364.829 ns | 124.7559 | 124.7559 | 124.7559 |  400146 B |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Once again, .NET 8 takes the gold!&lt;/p&gt;
&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;In this article we compared the performance of &lt;code&gt;ToArray&lt;/code&gt; versus &lt;code&gt;ToList&lt;/code&gt; and concluded the former performs better most of the time by being faster and more memory efficient, so consider using it when creating short lived collections were enumeration must be forced.&lt;/p&gt;
&lt;p&gt;We also concluded that .NET 8, the version still to be released, will bring fantastic performance upgrades in this regards. Not only it is significantly faster than the older versions, it also brings &lt;code&gt;ToArray&lt;/code&gt; and &lt;code&gt;ToList&lt;/code&gt; so close that it’s almost indifferent which method should be used.&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="csharp" />
        <category term="dotnet" />
        <category term="linq" />
        <updated>2023-11-09T00:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2023/11/08/NET-8-%E2%80%94-FrozenDictionary-performance/</id>
        <title>.NET 8 — FrozenDictionary performance</title>
        <link rel="alternate" href="https://code-corner.dev/2023/11/08/NET-8-%E2%80%94-FrozenDictionary-performance/"/>
        <content type="html">&lt;p&gt;Microsoft’s upcoming release of .NET 8 will introduce a lot of &lt;a href=&#34;https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8&#34;&gt;new features&lt;/a&gt; that will certainly be welcomed by the developer community, making it an even stronger framework for application development.&lt;/p&gt;
&lt;p&gt;One of those features is the namespace &lt;code&gt;System.Collections.Frozen&lt;/code&gt; that introduces two new collections: &lt;code&gt;FrozenDictionary&lt;/code&gt; and &lt;code&gt;FrozenSet&lt;/code&gt;. These new types, &lt;a href=&#34;https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8#performance-focused-types&#34;&gt;as stated by Microsoft&lt;/a&gt;, are focused in reducing the time of read operations at the expense of increasing initialization time of immutable collections. This makes them perfect for shared data that only needs to be populated a single time, like application configurations or cached data in-memory.&lt;/p&gt;
&lt;p&gt;In this article I’m going to benchmark the performance gains that can be achieved by using a &lt;code&gt;FrozenDictionary&lt;/code&gt; instead of a &lt;code&gt;Dictionary&lt;/code&gt; or an &lt;code&gt;ImmutableDictionary&lt;/code&gt; to store shared data.&lt;/p&gt;
&lt;p&gt;I’m going to use the well known C# library &lt;code&gt;BenchmarkDotNet&lt;/code&gt; to run the tests and the environment will be the following:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;BenchmarkDotNet v0.13.10, Windows 11 (10.0.22621.2428/22H2/2022Update/SunValley2)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;AMD Ryzen 7 3700X, 1 CPU, 16 logical and 8 physical cores&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;.NET SDK 8.0.100-rc.2.23502.2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  [Host]   : .NET 8.0.0 (8.0.23.47906), X64 RyuJIT AVX2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  .NET 8.0 : .NET 8.0.0 (8.0.23.47906), X64 RyuJIT AVX2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;For the tests, I’m going to benchmark the method &lt;code&gt;TryGetValue&lt;/code&gt;, with one for 100% key hits and another for 100% key misses, which should give the performance for both perfect and worse scenarios. The collections &lt;code&gt;FrozenDictionary&lt;/code&gt;, &lt;code&gt;ImmutableDictionary&lt;/code&gt; and &lt;code&gt;Dictionary&lt;/code&gt; will be used with a parameter setting the size.&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id=&#34;Scenario-1-—-all-keys-are-found&#34;&gt;&lt;a href=&#34;#Scenario-1-—-all-keys-are-found&#34; class=&#34;headerlink&#34; title=&#34;Scenario 1 — all keys are found&#34;&gt;&lt;/a&gt;Scenario 1 — all keys are found&lt;/h1&gt;&lt;p&gt;For this scenario I’m going to initialize a collection of unique keys, populate all three dictionaries accordingly and then run the &lt;code&gt;TryGetValue&lt;/code&gt; for all keys returning the latest found using the &lt;code&gt;Dictionary&lt;/code&gt; test as the baseline.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;SimpleJob(RuntimeMoniker.Net80)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;MemoryDiagnoser&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;TryGetValueAllKeysFound&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Params(10, 100, 1000, 10000, 100000)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; Size;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;[] _keys;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; Dictionary&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;, &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt; _dictionary;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; ImmutableDictionary&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;,&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt; _immutableDictionary;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; FrozenDictionary&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;, &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt; _frozenDictionary;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;GlobalSetup&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Setup&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; random = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Random(&lt;span class=&#34;number&#34;&gt;123&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; uniqueKeys = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; HashSet&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt;(Size);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;for&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; i = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;; i &amp;lt; Size; i++)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; key;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;do&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                key = random.Next();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125; &lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; (uniqueKeys.Contains(key));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uniqueKeys.Add(key);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _dictionary = uniqueKeys.Select((key, idx) =&amp;gt; (key, idx)).ToDictionary(e =&amp;gt; e.key, e =&amp;gt; e.idx);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _immutableDictionary = uniqueKeys.Select((key, idx) =&amp;gt; (key, idx)).ToImmutableDictionary(e =&amp;gt; e.key, e =&amp;gt; e.idx);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _frozenDictionary = uniqueKeys.Select((key, idx) =&amp;gt; (key, idx)).ToFrozenDictionary(e =&amp;gt; e.key, e =&amp;gt; e.idx);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _keys = uniqueKeys.ToArray();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark(Baseline = true)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Dictionary&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; latestValue = &lt;span class=&#34;number&#34;&gt;-1&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; key &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; _keys)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_dictionary.TryGetValue(key, &lt;span class=&#34;keyword&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                latestValue = &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; latestValue;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ImmutableDictionary&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; latestValue = &lt;span class=&#34;number&#34;&gt;-1&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; key &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; _keys)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_immutableDictionary.TryGetValue(key, &lt;span class=&#34;keyword&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                latestValue = &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; latestValue;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;FrozenDictionary&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; latestValue = &lt;span class=&#34;number&#34;&gt;-1&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; key &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; _keys)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_frozenDictionary.TryGetValue(key, &lt;span class=&#34;keyword&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                latestValue = &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; latestValue;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The benchmark results are the following:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Method              | Size   | Mean             | Error          | StdDev        | Ratio | RatioSD | Allocated | Alloc Ratio |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|-------------------- |------- |-----------------:|---------------:|--------------:|------:|--------:|----------:|------------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Dictionary          | 10     |         46.29 ns |       0.300 ns |      0.251 ns |  1.00 |    0.00 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ImmutableDictionary | 10     |         35.53 ns |       0.320 ns |      0.283 ns |  0.77 |    0.01 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| FrozenDictionary    | 10     |         85.47 ns |       1.779 ns |      5.244 ns |  1.86 |    0.13 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                     |        |                  |                |               |       |         |           |             |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Dictionary          | 100    |        470.00 ns |       2.253 ns |      2.108 ns |  1.00 |    0.00 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ImmutableDictionary | 100    |        736.28 ns |      14.637 ns |     31.820 ns |  1.57 |    0.07 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| FrozenDictionary    | 100    |        263.18 ns |       2.898 ns |      2.569 ns |  0.56 |    0.01 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                     |        |                  |                |               |       |         |           |             |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Dictionary          | 1000   |      4,993.04 ns |      27.440 ns |     22.913 ns |  1.00 |    0.00 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ImmutableDictionary | 1000   |     29,174.27 ns |     493.738 ns |    461.843 ns |  5.85 |    0.10 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| FrozenDictionary    | 1000   |      2,841.24 ns |      30.283 ns |     28.326 ns |  0.57 |    0.01 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                     |        |                  |                |               |       |         |           |             |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Dictionary          | 10000  |     77,394.04 ns |     295.972 ns |    247.150 ns |  1.00 |    0.00 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ImmutableDictionary | 10000  |    630,488.18 ns |   3,040.379 ns |  2,695.216 ns |  8.15 |    0.05 |       1 B |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| FrozenDictionary    | 10000  |     43,957.48 ns |     274.398 ns |    256.672 ns |  0.57 |    0.00 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                     |        |                  |                |               |       |         |           |             |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Dictionary          | 100000 |  1,126,207.11 ns |   5,941.069 ns |  5,266.603 ns |  1.00 |    0.00 |       1 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ImmutableDictionary | 100000 | 11,194,516.67 ns | 106,626.230 ns | 99,738.242 ns |  9.94 |    0.12 |      12 B |       12.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| FrozenDictionary    | 100000 |    806,004.77 ns |   5,670.946 ns |  5,304.606 ns |  0.72 |    0.01 |       1 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As you can see, except for very small collections, the &lt;code&gt;FrozenDictionary&lt;/code&gt; is about 43% faster than using a &lt;code&gt;Dictionary&lt;/code&gt;, much faster than an &lt;code&gt;ImmutableDictionary&lt;/code&gt;, without allocating more memory.&lt;/p&gt;
&lt;h1 id=&#34;Scenario-2-—-no-keys-are-found&#34;&gt;&lt;a href=&#34;#Scenario-2-—-no-keys-are-found&#34; class=&#34;headerlink&#34; title=&#34;Scenario 2 — no keys are found&#34;&gt;&lt;/a&gt;Scenario 2 — no keys are found&lt;/h1&gt;&lt;p&gt;For this scenario I’m going to initialize a collection of unique keys, populate all three dictionaries accordingly, convert all keys to negative numbers to ensure the lookups are all different but will never match, and then run the &lt;code&gt;TryGetValue&lt;/code&gt; for all returning the latest found using the &lt;code&gt;Dictionary&lt;/code&gt; test as the baseline.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;SimpleJob(RuntimeMoniker.Net80)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;MemoryDiagnoser&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;TryGetValueNoKeysFound&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Params(10, 100, 1000, 10000, 100000)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; Size;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;[] _keys;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; Dictionary&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;, &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt; _dictionary;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; ImmutableDictionary&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;,&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt; _immutableDictionary;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; FrozenDictionary&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;, &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt; _frozenDictionary;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;GlobalSetup&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Setup&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; random = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Random(&lt;span class=&#34;number&#34;&gt;123&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; uniqueKeys = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; HashSet&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt;(Size);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;for&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; i = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;; i &amp;lt; Size; i++)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; key;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;do&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                key = random.Next();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125; &lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; (uniqueKeys.Contains(key));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uniqueKeys.Add(key);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _dictionary = uniqueKeys.Select((key, idx) =&amp;gt; (key, idx)).ToDictionary(e =&amp;gt; e.key, e =&amp;gt; e.idx);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _immutableDictionary = uniqueKeys.Select((key, idx) =&amp;gt; (key, idx)).ToImmutableDictionary(e =&amp;gt; e.key, e =&amp;gt; e.idx);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _frozenDictionary = uniqueKeys.Select((key, idx) =&amp;gt; (key, idx)).ToFrozenDictionary(e =&amp;gt; e.key, e =&amp;gt; e.idx);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// convert all keys to negative numbers to ensure no matches&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _keys = uniqueKeys.Select(key =&amp;gt; -key).ToArray();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark(Baseline = true)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Dictionary&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; latestValue = &lt;span class=&#34;number&#34;&gt;-1&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; key &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; _keys)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_dictionary.TryGetValue(key, &lt;span class=&#34;keyword&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                latestValue = &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; latestValue;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ImmutableDictionary&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; latestValue = &lt;span class=&#34;number&#34;&gt;-1&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; key &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; _keys)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_immutableDictionary.TryGetValue(key, &lt;span class=&#34;keyword&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                latestValue = &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; latestValue;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;Benchmark&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;FrozenDictionary&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; latestValue = &lt;span class=&#34;number&#34;&gt;-1&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; key &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; _keys)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_frozenDictionary.TryGetValue(key, &lt;span class=&#34;keyword&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                latestValue = &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; latestValue;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The benchmark results are the following:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;| Method              | Size   | Mean            | Error        | StdDev       | Ratio | RatioSD | Allocated | Alloc Ratio |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|-------------------- |------- |----------------:|-------------:|-------------:|------:|--------:|----------:|------------:|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Dictionary          | 10     |        37.13 ns |     0.740 ns |     0.823 ns |  1.00 |    0.00 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ImmutableDictionary | 10     |        33.81 ns |     0.629 ns |     0.588 ns |  0.91 |    0.03 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| FrozenDictionary    | 10     |        19.16 ns |     0.129 ns |     0.114 ns |  0.51 |    0.01 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                     |        |                 |              |              |       |         |           |             |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Dictionary          | 100    |       396.11 ns |     2.697 ns |     2.522 ns |  1.00 |    0.00 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ImmutableDictionary | 100    |       488.48 ns |     2.480 ns |     2.198 ns |  1.23 |    0.01 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| FrozenDictionary    | 100    |       218.70 ns |     1.530 ns |     1.431 ns |  0.55 |    0.00 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                     |        |                 |              |              |       |         |           |             |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Dictionary          | 1000   |     4,019.84 ns |    38.187 ns |    35.720 ns |  1.00 |    0.00 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ImmutableDictionary | 1000   |     6,708.41 ns |    14.893 ns |    13.931 ns |  1.67 |    0.02 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| FrozenDictionary    | 1000   |     2,372.01 ns |    15.113 ns |    13.398 ns |  0.59 |    0.00 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                     |        |                 |              |              |       |         |           |             |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Dictionary          | 10000  |    83,803.51 ns |   232.996 ns |   206.545 ns |  1.00 |    0.00 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ImmutableDictionary | 10000  |    88,593.46 ns |   763.681 ns |   714.347 ns |  1.06 |    0.01 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| FrozenDictionary    | 10000  |    29,071.40 ns |    85.885 ns |    76.135 ns |  0.35 |    0.00 |         - |          NA |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                     |        |                 |              |              |       |         |           |             |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| Dictionary          | 100000 | 1,293,765.26 ns | 3,587.916 ns | 3,356.139 ns |  1.00 |    0.00 |       1 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| ImmutableDictionary | 100000 | 1,145,366.16 ns | 4,726.433 ns | 4,421.108 ns |  0.89 |    0.00 |       1 B |        1.00 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| FrozenDictionary    | 100000 |   475,573.09 ns | 2,445.081 ns | 2,167.501 ns |  0.37 |    0.00 |         - |        0.00 |&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As you can see, the &lt;code&gt;FrozenDictionary&lt;/code&gt; is on average 47% faster than a &lt;code&gt;Dictionary&lt;/code&gt; for all sizes and even uses less memory for bigger collections. The &lt;code&gt;ImmutableDictionary&lt;/code&gt;, once again, is the worse performer.&lt;/p&gt;
&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;Microsoft with the release of .NET 8 is, once again, providing a lot of performance improvements that developers can use in their applications.&lt;/p&gt;
&lt;p&gt;For this article in particular, if you are storing key-value reference data that is immutable, populated a single time and can be shared across your application, the &lt;code&gt;FrozenDictionary&lt;/code&gt; may be a good option if performance is of concern.&lt;/p&gt;
&lt;p&gt;Soon I’ll also do a benchmark for &lt;code&gt;FrozenSet&lt;/code&gt; which I expect should demonstrate similar results.&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="csharp" />
        <updated>2023-11-08T00:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2023/11/04/Understanding-Flag-Enums-in-C/</id>
        <title>Understanding Flag Enums in C#</title>
        <link rel="alternate" href="https://code-corner.dev/2023/11/04/Understanding-Flag-Enums-in-C/"/>
        <content type="html">&lt;p&gt;Enumeration types, also known as enum types, are widely used by C# developers to improve code readability and maintainability, by offering a standardized way to represent a set of related numeric constants. Good examples are days of the week, seasons, or a predefined range of colors.&lt;/p&gt;
&lt;p&gt;In this article I’m going to talk about flag enums, which are a special case, that can be used to represent a combination of binary choices into a single value using bitwise operations.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Imagine you were creating a simple alarm application were you could set alarms for a given time and weekdays. If you were using a simple enum, the &lt;code&gt;Alarm&lt;/code&gt; class would need a collection to store the selected days, ideally an hash set to prevent duplicates and faster lookups. It would look similar to this:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;enum&lt;/span&gt; Weekday&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    None = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Sunday,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Monday,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Tuesday,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Wednesday,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Thursday,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Friday,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Saturday,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Alarm&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; ICollection&amp;lt;Weekday&amp;gt; Weekdays &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &amp;#125; = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; HashSet&amp;lt;Weekday&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; TimeOnly Time &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;An an example, if you needed to create an alarm to set off at 7 a.m. only during workdays and needed to execute some conditional logic depending your selection, the code could be as follows:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; alarm = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Alarm&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Time = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; TimeOnly(&lt;span class=&#34;number&#34;&gt;07&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;00&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;00&lt;/span&gt;),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Weekdays =&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Weekday.Monday,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Weekday.Tuesday,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Weekday.Wednesday,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Weekday.Thursday,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Weekday.Friday,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Weekday.Saturday,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// disable a flag&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;alarm.Weekdays.Remove(Weekday.Saturday);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// check if a flag is enabled&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (alarm.Weekdays.Contains(Weekday.Monday))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// check if some flags are enabled&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (alarm.Weekdays.Any(e =&amp;gt; e &lt;span class=&#34;keyword&#34;&gt;is&lt;/span&gt; Weekday.Saturday &lt;span class=&#34;keyword&#34;&gt;or&lt;/span&gt; Weekday.Sunday))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// check if a flag is disabled&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!alarm.Weekdays.Contains(Weekday.Friday))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The code is relatively simple and easy to understand but it has two drawbacks — memory allocations of new collections and constant iterations to check for flags. How can we solve this?&lt;/p&gt;
&lt;h1 id=&#34;Introducing-flag-enums&#34;&gt;&lt;a href=&#34;#Introducing-flag-enums&#34; class=&#34;headerlink&#34; title=&#34;Introducing flag enums&#34;&gt;&lt;/a&gt;Introducing flag enums&lt;/h1&gt;&lt;p&gt;Flag enums were created to optimize both memory and CPU usage by providing a way to store multiple options into a single variable.&lt;/p&gt;
&lt;p&gt;The core concept is the following: every variable is stored in memory as binary data and CPUs are very efficient at making bitwise operations so, if you ensure each enum value is represented by a single bit that doesn’t overlap another value, you can use binary operators (AND, OR, NOT and XOR) to store multiple options into a single primitive variable.&lt;/p&gt;
&lt;p&gt;By default, enums are represented in memory as an integer, but can also be a short or a long value. If each bit represents a different enum value, this means you can have up to 64 possible values that must not overlap each other.&lt;/p&gt;
&lt;p&gt;Lets change the previous example to use a flag enum instead:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;Flags&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;enum&lt;/span&gt; Weekday&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    None        = &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Sunday      = &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;comment&#34;&gt;// 0b_00000001 // 1&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Monday      = &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;comment&#34;&gt;// 0b_00000010 // 2&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Tuesday     = &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;comment&#34;&gt;// 0b_00000100 // 4&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Wednesday   = &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&#34;number&#34;&gt;3&lt;/span&gt;, &lt;span class=&#34;comment&#34;&gt;// 0b_00001000 // 8&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Thursday    = &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&#34;number&#34;&gt;4&lt;/span&gt;, &lt;span class=&#34;comment&#34;&gt;// 0b_00010000 // 16&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Friday      = &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&#34;number&#34;&gt;5&lt;/span&gt;, &lt;span class=&#34;comment&#34;&gt;// 0b_00100000 // 32&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Saturday    = &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&#34;number&#34;&gt;6&lt;/span&gt;, &lt;span class=&#34;comment&#34;&gt;// 0b_01000000 // 64&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Alarm&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Weekday Weekdays &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; TimeOnly Time &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As you can see, some small changes were made:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Added the &lt;code&gt;Flags&lt;/code&gt; attribute to the enum — not a requirement, but I’ll explain later why it should be used;&lt;/li&gt;
&lt;li&gt;Defined the value of each option ensuring no bit was overlapped — in this case I used the &lt;a href=&#34;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators#left-shift-operator-&#34;&gt;shift left&lt;/a&gt; operator to make it easier and more readable;&lt;/li&gt;
&lt;li&gt;Removed the collection and changed the property &lt;code&gt;Weekdays&lt;/code&gt; to a simple &lt;code&gt;Weekday&lt;/code&gt; enum;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You may now be asking: you removed the collection and changed it to a single &lt;code&gt;Weekday&lt;/code&gt; value, how am I supposed to represent multiple options then?&lt;/p&gt;
&lt;h1 id=&#34;Introducing-bitwise-operators&#34;&gt;&lt;a href=&#34;#Introducing-bitwise-operators&#34; class=&#34;headerlink&#34; title=&#34;Introducing bitwise operators&#34;&gt;&lt;/a&gt;Introducing bitwise operators&lt;/h1&gt;&lt;p&gt;As I stated before, bitwise operators (&lt;a href=&#34;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators#logical-and-operator-&#34;&gt;AND&lt;/a&gt;, &lt;a href=&#34;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators#logical-or-operator-&#34;&gt;OR&lt;/a&gt;, &lt;a href=&#34;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators#logical-exclusive-or-operator-&#34;&gt;XOR&lt;/a&gt;, &lt;a href=&#34;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators#bitwise-complement-operator-&#34;&gt;NOT&lt;/a&gt;) allow us to execute logical computation between the bits of two integral operands.&lt;/p&gt;
&lt;p&gt;This is relevant because if we use a bitwise OR (|) operator we can store the result as an aggregation of multiple active flags. Imagine you wanted a variable representing the weekend (setting Saturday and Sunday flags):&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; weekend = Weekday.Saturday | Weekday.Sunday;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;//          = 0b_01000000 | 0b_00000001&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;//          = 0b_01000001&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As you can see in the comment showing the binary representation, both flags will be set.&lt;/p&gt;
&lt;p&gt;On the other hand, if you want to check if a given flag is active you can simply use the bitwise AND (&amp;amp;) operator and compare the result to zero (or &lt;code&gt;Weekday.None&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Lets check if Sunday is part of the weekend by checking a non zero result:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; hasSunday = (weekend &amp;amp; Weekday.Sunday) != Weekday.None;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;//            = (0b_01000001 &amp;amp; 0b_00000001) != 0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;//            = 0b_00000001 != 0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;//            = true&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you want to check if a flag is disabled the operation result must be zero:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; noMonday = (weekend &amp;amp; Weekday.Monday) == Weekday.None;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;//           = (0b_01000001 &amp;amp; 0b_00000010) == 0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;//           = 0b_00000000 == 0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;//           = true&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;And finally, if you want to disable a given flag just do a bitwise AND against a negated flag value:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; sunday = weekend &amp;amp; ~Weekday.Saturday;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;//         = 0b_01000001 &amp;amp; ~0b_01000000&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;//         = 0b_01000001 &amp;amp; 0b_10111111&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;//         = 0b_00000001&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Lets update the original example and use the bitwise operators we just learned (AND, OR, NOT):&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; alarm = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Alarm&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Time = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; TimeOnly(&lt;span class=&#34;number&#34;&gt;07&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;00&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;00&lt;/span&gt;),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Weekdays = Weekday.Monday |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;               Weekday.Tuesday |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;               Weekday.Wednesday |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;               Weekday.Thursday |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;               Weekday.Friday |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;               Weekday.Saturday,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// Weekdays = (Weekday) 0b_01111110&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// disable a flag&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;alarm.Weekdays &amp;amp;= ~Weekday.Saturday; &lt;span class=&#34;comment&#34;&gt;// (Weekday) 0b_00111110&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// check if a flag is enabled&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; ((alarm.Weekdays &amp;amp; Weekday.Monday) != &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// check if some flags are enabled&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; ((alarm.Weekdays &amp;amp; (Weekday.Saturday | Weekday.Sunday)) != Weekday.None)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// check if a flag is disabled&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; ((alarm.Weekdays &amp;amp; Weekday.Friday) == Weekday.None)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Right now you must be thinking: sure, this code is more resource efficient and performant but it certainly is harder to read… not sure if I gained something… how can I improve this?&lt;/p&gt;
&lt;p&gt;Well, because checking if a flag is active is a very common scenario, all enums have a method called &lt;code&gt;HasFlag&lt;/code&gt; that can be used just for that. Lets rewrite the example in a much simpler and readable way:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; alarm = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Alarm&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Time = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; TimeOnly(&lt;span class=&#34;number&#34;&gt;07&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;00&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;00&lt;/span&gt;),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Weekdays = Weekday.Monday |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;               Weekday.Tuesday |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;               Weekday.Wednesday |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;               Weekday.Thursday |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;               Weekday.Friday |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;               Weekday.Saturday,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;alarm.Weekdays &amp;amp;= ~Weekday.Saturday;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (alarm.Weekdays.HasFlag(Weekday.Monday))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (alarm.Weekdays.HasFlag(Weekday.Saturday) || alarm.Weekdays.HasFlag(Weekday.Sunday))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!alarm.Weekdays.HasFlag(Weekday.Friday))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;It would be nice to also have a method to disable a flag but none is provided by the framework, so you’ll have to stick with bitwise operators for that scenario.&lt;/p&gt;
&lt;h1 id=&#34;Why-the-Flags-attribute&#34;&gt;&lt;a href=&#34;#Why-the-Flags-attribute&#34; class=&#34;headerlink&#34; title=&#34;Why the [Flags] attribute?&#34;&gt;&lt;/a&gt;Why the [Flags] attribute?&lt;/h1&gt;&lt;p&gt;Earlier I said marking the enum with a &lt;code&gt;Flags&lt;/code&gt; attribute is opcional (everything we talked about will still work) but still recommended because it helps the IDE to give you more detailed warnings or suggestions, but it’s specially important for debugging, parsing or converting to a string representation.&lt;/p&gt;
&lt;p&gt;Imagine the &lt;code&gt;weekend&lt;/code&gt; scenario were both Saturday and Sunday flags are set. If the enum doesn’t have the &lt;code&gt;Flags&lt;/code&gt; attribute and you go inspect the variable, the IDE will show the value 65 (&lt;code&gt;0b_01000001&lt;/code&gt;) because it isn’t defined in the enum. Same behavior for the &lt;code&gt;ToString&lt;/code&gt; method, that will return &lt;code&gt;&amp;quot;65&amp;quot;&lt;/code&gt;:&lt;/p&gt;
&lt;img src=&#34;/2023/11/04/Understanding-Flag-Enums-in-C/01_value_no_flag_attribute.png&#34; class=&#34;post-image&#34;&gt;

&lt;p&gt;If on the contrary the &lt;code&gt;Flags&lt;/code&gt; attribute is used, both IDE and &lt;code&gt;ToString&lt;/code&gt; method will show a proper representation of what flags are active:&lt;/p&gt;
&lt;img src=&#34;/2023/11/04/Understanding-Flag-Enums-in-C/02_value_flag_attribute.png&#34; class=&#34;post-image&#34;&gt;

&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;I hope this article gave you a good idea on how to use flag enums and bitwise operations to store and check for active flags using a single field, making your application more resource efficient without losing code readability.&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="csharp" />
        <category term="dotnet" />
        <updated>2023-11-04T00:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2022/08/13/Immutability-and-Entity-Framework-Core/</id>
        <title>Immutability and Entity Framework Core</title>
        <link rel="alternate" href="https://code-corner.dev/2022/08/13/Immutability-and-Entity-Framework-Core/"/>
        <content type="html">&lt;p&gt;Nowadays, when implementing a .NET application that works directly with a database (relational or not), most developers will chose Entity Framework Core to abstract their data layer and work directly with entity classes.&lt;/p&gt;
&lt;p&gt;It has become an integral part of the .NET ecosystem, just like ASP.NET, and it is extremely rare finding someone that never worked with it. I’ve been using it myself ever since version 4 (along with other ORMs) and I must say that it &lt;em&gt;&lt;strong&gt;aged like a fine wine&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Fully open sourced, with support for multiple databases (not just SQL Server) while offering relatively optimized and extensible conversion from LINQ to database queries, accessing data from a .NET application never been easier. And when it doesn’t support something, &lt;a href=&#34;/2021/03/09/Integrating-Dapper-with-Entity-Framework-Core/&#34; title=&#34;Integrating Dapper with Entity Framework Core&#34;&gt;just integrate with some micro ORM and go crazy on that SQL&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;One core feature is the implementation of the unit of work pattern by supporting, what is usually called, a first level cache. Load a bunch of entities from the database, that will be tracked in memory by the context, mutate or delete them, create new ones and then flush everything in a single database access.&lt;/p&gt;
&lt;p&gt;The thing about this feature, despite working fine most of the time, is that it depends on managing internal state with mutable entities. After all, it was originally focused on C# developers that were used to work in a object-oriented way — get an entity, change some properties, request an update.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But what if you are a C# developer and prefer the advantages provided by immutability?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Lets look at the most used immutable class in the .NET world — the string.&lt;/p&gt;
&lt;p&gt;We all know that working with text can be memory inefficient if badly managed, but imagine a world were you could initialize a string with the name “Bruce Wayne”, pass it as an argument to some method that was supposed to count how many words were in it, and when you realize, your original string contains the name “Peter Parker” because the strings were mutable and nothing could prevent that?&lt;/p&gt;
&lt;p&gt;In this article I won’t enter into details about the advantages of immutable over mutable objects, and vice versa. There are great articles already explaining both visions and we all know no size fits all, so it kinda depends of your current needs.&lt;/p&gt;
&lt;p&gt;But I’m going to explain how you can use immutable entities directly with Entity Framework Core so you know that not only it is possible but also a viable option.&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id=&#34;Immutable-entities-in-C&#34;&gt;&lt;a href=&#34;#Immutable-entities-in-C&#34; class=&#34;headerlink&#34; title=&#34;Immutable entities in C#&#34;&gt;&lt;/a&gt;Immutable entities in C#&lt;/h1&gt;&lt;p&gt;Before C# 9 the only way to create an immutable entity was to define a class or structure with &lt;em&gt;getter only properties&lt;/em&gt; that were initialized during object construction, ensuring nothing could be changed afterwards.&lt;/p&gt;
&lt;p&gt;As an example, a &lt;code&gt;PersonEntity&lt;/code&gt; would look like this:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;PersonEntity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;PersonEntity&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; forename, &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; surname, &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;? middleNames = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;, DateOnly? birthdate = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Forename = forename;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Surname = surname;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        MiddleNames = middleNames;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Birthdate = birthdate;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Forename &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Surname &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;? MiddleNames &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; DateOnly? Birthdate &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;It works as expected but there is one major problem: just by looking at the code, the developer cannot tell which properties are being set unless argument names are used and, if it wants to change something, it must copy every property by hand.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; person = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; PersonEntity(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;string&#34;&gt;&amp;quot;Bruce&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;string&#34;&gt;&amp;quot;Wayne&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;person = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; PersonEntity(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    person.Forename,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    person.Surname,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;string&#34;&gt;&amp;quot;Thomas&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; DateOnly(&lt;span class=&#34;number&#34;&gt;1972&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;02&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;19&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;For small POCOs this may not be a problem, but for bigger ones the chances of someone making a mistake increases.&lt;/p&gt;
&lt;p&gt;With this in mind, Microsoft implemented &lt;a href=&#34;https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/init&#34;&gt;init only setters&lt;/a&gt; supporting object initializers while still preventing changes afterwards. Couple this with &lt;a href=&#34;https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records&#34;&gt;record types&lt;/a&gt;, and creating immutable entities has never been easier in C#.&lt;/p&gt;
&lt;p&gt;The same PersonEntity, but now using both of these features:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;record&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;PersonEntity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Forename &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Surname &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;? MiddleNames &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; DateOnly? Birthdate &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Not only the syntax is more concise by using auto-implemented properties, you can now easily tell which properties are being set during initialization and have a much easier life copying data by using the keyword with and only state which properties must change.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; person = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; PersonEntity&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Forename = &lt;span class=&#34;string&#34;&gt;&amp;quot;Bruce&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Surname = &lt;span class=&#34;string&#34;&gt;&amp;quot;Wayne&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;person = person &lt;span class=&#34;keyword&#34;&gt;with&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    MiddleNames = &lt;span class=&#34;string&#34;&gt;&amp;quot;Thomas&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Birthdate = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; DateOnly(&lt;span class=&#34;number&#34;&gt;1972&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;02&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;19&lt;/span&gt;),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Change-Tracking-and-immutability&#34;&gt;&lt;a href=&#34;#Change-Tracking-and-immutability&#34; class=&#34;headerlink&#34; title=&#34;Change Tracking and immutability&#34;&gt;&lt;/a&gt;Change Tracking and immutability&lt;/h1&gt;&lt;p&gt;Every developer that uses Entity Framework knows it provides a lot of database abstractions to enable working directly with .NET objects.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://docs.microsoft.com/en-us/ef/core/change-tracking&#34;&gt;One core feature is the automatic detection of changes made to entities&lt;/a&gt;. This usually means interacting with the context to get an existing entity, change some properties and then invoke &lt;code&gt;SaveChangesAsync&lt;/code&gt; which will generate a bunch of instructions and execute them into the database. Under the hood, when the entity is retrieved from the context it will keep a reference to the instance and original values from the database and when the developer requests the context to save changes, all properties of tracked entities will be compared and if anything changed, instructions will be generated and executed.&lt;/p&gt;
&lt;p&gt;This works well for mutable entities but we are implementing immutability, and since no changes will be made to tracked entities, will this cause any issues?&lt;/p&gt;
&lt;p&gt;Let’s imagine the following use case:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You retrieve our &lt;em&gt;Bruce Wayne&lt;/em&gt; from the database using an EF Core context, which internally will keep a reference to it;&lt;/li&gt;
&lt;li&gt;You want to change it’s birthdate, so you clone it while assigning a new date;&lt;/li&gt;
&lt;li&gt;If you invoke &lt;code&gt;SaveChangesAsync&lt;/code&gt;, nothing will happen because no changes happened to the original entity;&lt;/li&gt;
&lt;li&gt;If you invoke &lt;code&gt;Update&lt;/code&gt;, an &lt;code&gt;InvalidOperationException&lt;/code&gt; will be thrown because the context is already tracking an entity with the same primary key;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Certainly invoking &lt;code&gt;SaveChangesAsync&lt;/code&gt; and nothing happening was expected, after all we created a copy of the original instance, which EF Core knows nothing about and would never automatically detect changes, but why the exception when trying to attach the entity to the context?&lt;/p&gt;
&lt;p&gt;This leads to another EF Core feature, called &lt;a href=&#34;https://docs.microsoft.com/en-us/ef/core/change-tracking/identity-resolution&#34;&gt;Identity Resolution&lt;/a&gt;, strongly correlated to our change tracking.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Identity Resolution&lt;/em&gt; ensures the same entity is retrieved for the same primary key while being tracked. This is a requirement when implementing the Unit of Work pattern because EF Core only flushes data when &lt;code&gt;SaveChangesAsync&lt;/code&gt; is invoked.&lt;/p&gt;
&lt;p&gt;Again, let’s imagine our &lt;code&gt;PersonEntity&lt;/code&gt; was a mutable class:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You retrieve our &lt;em&gt;Bruce Wayne&lt;/em&gt; from the database using an EF Core context, which internally will keep a reference to it, also identified by its primary key;&lt;/li&gt;
&lt;li&gt;You change its birthdate;&lt;/li&gt;
&lt;li&gt;You do some more work;&lt;/li&gt;
&lt;li&gt;You try to get it again from the EF Core context but since that primary key its already being tracked, it returns the same instance instead of going to the database and returning old data for your Unit of Work operation;&lt;/li&gt;
&lt;li&gt;You change more properties;&lt;/li&gt;
&lt;li&gt;Invoke &lt;code&gt;SaveChangesAsync&lt;/code&gt;, flushing changes and now both the instance and database have the same data;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see, &lt;em&gt;Identity Resolution&lt;/em&gt; is important for mutable entities, but not so much for our use case and it’s a problem we must solve.&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id=&#34;The-project&#34;&gt;&lt;a href=&#34;#The-project&#34; class=&#34;headerlink&#34; title=&#34;The project&#34;&gt;&lt;/a&gt;The project&lt;/h1&gt;&lt;p&gt;It seems the only thing preventing an immutable approach to database access while using EF Core is not relying on &lt;em&gt;Change Tracking&lt;/em&gt; for mutations while preventing &lt;em&gt;Identity Resolution&lt;/em&gt; problems.&lt;/p&gt;
&lt;p&gt;Let’s create a C# console application that will use Entity Framework Core to store data into a SQLite database, using immutable &lt;code&gt;record&lt;/code&gt; entities.&lt;/p&gt;
&lt;p&gt;The source code is available on &lt;a href=&#34;https://github.com/gravity00/efcore-immutability&#34;&gt;GitHub&lt;/a&gt;, feel free to give it a look.&lt;/p&gt;
&lt;h2 id=&#34;Setup&#34;&gt;&lt;a href=&#34;#Setup&#34; class=&#34;headerlink&#34; title=&#34;Setup&#34;&gt;&lt;/a&gt;Setup&lt;/h2&gt;&lt;p&gt;Start by opening Visual Studio and creating a &lt;strong&gt;.NET 6.0 Console Application&lt;/strong&gt; with a name at any location you prefer.&lt;/p&gt;
&lt;img src=&#34;/2022/08/13/Immutability-and-Entity-Framework-Core/01_project_create.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Install most recent versions of Entity Framework Core for SQLite and Microsoft hosting nugets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Extensions.Hosting&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.EntityFrameworkCore.Sqlite&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;img src=&#34;/2022/08/13/Immutability-and-Entity-Framework-Core/02_nugets.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Create a &lt;code&gt;ProgramHost&lt;/code&gt; class implementing &lt;code&gt;IHostedService&lt;/code&gt;. This class will run our exemple code, but for now just inject a logger and write something inside the &lt;code&gt;StartAsync&lt;/code&gt; method. We’ll do nothing in the &lt;code&gt;StopAsync&lt;/code&gt; method, so just return a completed task.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProgramHost&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IHostedService&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; ILogger&amp;lt;ProgramHost&amp;gt; _logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProgramHost&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        ILogger&amp;lt;ProgramHost&amp;gt; logger&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger = logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;StartAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogInformation(&lt;span class=&#34;string&#34;&gt;&amp;quot;I&amp;#x27;m alive!&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;StopAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct&lt;/span&gt;)&lt;/span&gt; =&amp;gt; Task.CompletedTask;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Open the &lt;code&gt;Program.cs&lt;/code&gt; file, build an host with our &lt;code&gt;ProgramHost&lt;/code&gt; class registered as a hosted service.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; host = Host.CreateDefaultBuilder()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    .ConfigureServices(services =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddHostedService&amp;lt;ProgramHost&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    .Build();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; host.RunAsync();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you configured everything correctly and run the application, you should see something like this:&lt;/p&gt;
&lt;img src=&#34;/2022/08/13/Immutability-and-Entity-Framework-Core/03_setup_logs.png&#34; class=&#34;&#34;&gt;

&lt;h2 id=&#34;The-database-model&#34;&gt;&lt;a href=&#34;#The-database-model&#34; class=&#34;headerlink&#34; title=&#34;The database model&#34;&gt;&lt;/a&gt;The database model&lt;/h2&gt;&lt;p&gt;For simplicity, we’ll have a single Persons table:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Id&lt;/strong&gt; — identity column to uniquely identify the row (required);&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Forename&lt;/strong&gt; — stores the first name (text, required);&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Surname&lt;/strong&gt; — stores the last name (text, required);&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MiddleNames&lt;/strong&gt; — stores the middle names (text, optional);&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Birthdate&lt;/strong&gt; — stores the date of birth (date, optional);&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When representing database entities, I always create a base class containing properties to be defined in all entities (like the unique identifier or some metadata columns), and for the rest I always use positional records syntax for required properties and auto-properties for optional ones, making clear to the developer which ones must be always provided (this also removes compiler warnings if the project is configured for nullable reference types).&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; &lt;span class=&#34;doctag&#34;&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; Base class for all database entities&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; &lt;span class=&#34;doctag&#34;&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;record&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Entity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; &lt;span class=&#34;doctag&#34;&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; Unique identifier&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; &lt;span class=&#34;doctag&#34;&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; Id &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; &lt;span class=&#34;doctag&#34;&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; Representation of a person&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; &lt;span class=&#34;doctag&#34;&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; &lt;span class=&#34;doctag&#34;&gt;&amp;lt;param name=&amp;quot;Forename&amp;quot;&amp;gt;&lt;/span&gt;Person first name&lt;span class=&#34;doctag&#34;&gt;&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; &lt;span class=&#34;doctag&#34;&gt;&amp;lt;param name=&amp;quot;Surname&amp;quot;&amp;gt;&lt;/span&gt;Person last name&lt;span class=&#34;doctag&#34;&gt;&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;record&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;PersonEntity&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Forename,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Surname&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;) : Entity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; &lt;span class=&#34;doctag&#34;&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; Person middle names, separated by spaces&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; &lt;span class=&#34;doctag&#34;&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;? MiddleNames &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; &lt;span class=&#34;doctag&#34;&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; Person date of birth&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;&lt;span class=&#34;doctag&#34;&gt;///&lt;/span&gt; &lt;span class=&#34;doctag&#34;&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; DateOnly? Birthdate &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;init&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you prefer to keep everything more compact, feel free to use &lt;em&gt;positional records&lt;/em&gt; with default values for optional properties.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;record&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;PersonEntity&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Forename,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Surname,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;? MiddleNames = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    DateOnly? Birthdate = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;&lt;/span&gt;) : Entity&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;This is the most compact syntax, but be careful when adding new optional properties if your are sharing &lt;em&gt;records&lt;/em&gt; across multiple applications. It will also be considered a breaking change unless you define both constructors, effectively losing this compact syntax. That’s the reason I don’t use &lt;em&gt;positional record syntax&lt;/em&gt; for everything, but it kinda depends on your needs.&lt;/p&gt;
&lt;p&gt;Now, lets create our EF Core context with &lt;code&gt;Persons&lt;/code&gt; table mappings.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SampleDbContext&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;DbContext&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SampleDbContext&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;DbContextOptions&amp;lt;SampleDbContext&amp;gt; options&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;        : &lt;span class=&#34;title&#34;&gt;base&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;options&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;OnModelCreating&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;ModelBuilder builder&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;base&lt;/span&gt;.OnModelCreating(builder);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        builder.Entity&amp;lt;PersonEntity&amp;gt;(cfg =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.ToTable(&lt;span class=&#34;string&#34;&gt;&amp;quot;Persons&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasKey(e =&amp;gt; e.Id);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Id)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .ValueGeneratedOnAdd();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Forename)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .HasMaxLength(&lt;span class=&#34;number&#34;&gt;64&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Surname)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .HasMaxLength(&lt;span class=&#34;number&#34;&gt;64&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.MiddleNames)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .HasMaxLength(&lt;span class=&#34;number&#34;&gt;256&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Birthdate);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Open the &lt;code&gt;Program.cs&lt;/code&gt; file and register the EF Core context into the container. This is a test console, so I’ll use the temporary folder to store the SQLite file but feel free to use any other location and file name.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; host = Host.CreateDefaultBuilder()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    .ConfigureServices(services =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddDbContext&amp;lt;SampleDbContext&amp;gt;(options =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; connectionString = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; SqliteConnectionStringBuilder&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                DataSource = Path.Combine(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    Path.GetTempPath(),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;string&#34;&gt;&amp;quot;efcore-immutability-sample.sqlite3&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                )&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;.ConnectionString;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            options.UseSqlite(connectionString);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddHostedService&amp;lt;ProgramHost&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    .Build();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; host.RunAsync();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now inject the context into the &lt;code&gt;ProgramHost&lt;/code&gt; class and, since this is a test console and we want to freely modify our entities without much thought, change the &lt;code&gt;StartAsync&lt;/code&gt; method to always drop and recreate the database on startup.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProgramHost&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IHostedService&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; ILogger&amp;lt;ProgramHost&amp;gt; _logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; SampleDbContext _context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProgramHost&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        ILogger&amp;lt;ProgramHost&amp;gt; logger,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        SampleDbContext context&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger = logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _context = context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;StartAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Ensuring database is in a clean state&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.EnsureDeletedAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.EnsureCreatedAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;StopAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct&lt;/span&gt;)&lt;/span&gt; =&amp;gt; Task.CompletedTask;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you run the application and open the SQLite database file (using DB Browser for SQLite or equivalent), both a &lt;code&gt;Persons&lt;/code&gt; and &lt;code&gt;sqlite_sequence&lt;/code&gt; tables should be defined.&lt;/p&gt;
&lt;img src=&#34;/2022/08/13/Immutability-and-Entity-Framework-Core/04_db_setup.png&#34; class=&#34;&#34;&gt;

&lt;h2 id=&#34;Configuring-for-immutability&#34;&gt;&lt;a href=&#34;#Configuring-for-immutability&#34; class=&#34;headerlink&#34; title=&#34;Configuring for immutability&#34;&gt;&lt;/a&gt;Configuring for immutability&lt;/h2&gt;&lt;p&gt;Now that we have a working solution lets recall what we need to achieve to support immutability:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Not relying on &lt;em&gt;Change Tracking&lt;/em&gt; to know which instructions must be executed when &lt;code&gt;SaveChangesAsync&lt;/code&gt; is invoked;&lt;/li&gt;
&lt;li&gt;Preventing &lt;em&gt;Identity Resolution&lt;/em&gt; exceptions;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because &lt;em&gt;Change Tracking&lt;/em&gt; is such a core feature in Entity Framework Core, you can only disable it when querying for entities.&lt;/p&gt;
&lt;p&gt;As stated in Microsoft &lt;a href=&#34;https://docs.microsoft.com/en-us/ef/core/querying/tracking&#34;&gt;documentation&lt;/a&gt;, we can disable it in three ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using extension method &lt;code&gt;AsNoTracking&lt;/code&gt; for each query;&lt;/li&gt;
&lt;li&gt;Setting context property &lt;code&gt;ChangeTracker.QueryTrackingBehavior&lt;/code&gt; to &lt;code&gt;NoTracking&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Global configuration with method &lt;code&gt;UseQueryTrackingBehavior&lt;/code&gt;;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Open &lt;code&gt;Program.cs&lt;/code&gt; file and disable it globally, ensuring will never be tracked when queried from the database.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; host = Host.CreateDefaultBuilder()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    .ConfigureServices(services =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddDbContext&amp;lt;SampleDbContext&amp;gt;(options =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; connectionString = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; SqliteConnectionStringBuilder&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                DataSource = Path.Combine(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    Path.GetTempPath(),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;string&#34;&gt;&amp;quot;efcore-immutability-sample.sqlite3&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                )&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;.ConnectionString;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            options.UseSqlite(connectionString)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddHostedService&amp;lt;ProgramHost&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    .Build();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; host.RunAsync();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;In theory, if you only made a single entity operation per unit of work, like getting a person by unique identifier, changing its middle names and doing an update, this configuration would be enough.&lt;/p&gt;
&lt;p&gt;Sadly we all know development is far from perfect and more complex applications, due to business requirements, may lead to multiple updates to the same data in a single unit of work operation. That’s one of the major reasons behind &lt;em&gt;Change Tracking&lt;/em&gt; in EF Core, to implement the unit of work pattern while not relying on database ACID implementations and reducing the time transactions stays open.&lt;/p&gt;
&lt;p&gt;This means every time an entity is added, updated or removed using the context, an internal reference will be kept and &lt;em&gt;Identity Resolution&lt;/em&gt; exceptions will be thrown if someone tries to attach an entity with the same id.&lt;/p&gt;
&lt;p&gt;There are multiple ways to solve this problem but the easiest one is to enforce a transaction, flush changes to the database every time a create, update or delete is requested and then detach the entity.&lt;/p&gt;
&lt;p&gt;Since this is a proof of concept application, I’m going to implementing this behavior with extension methods over &lt;code&gt;DbContext&lt;/code&gt; instances, but feel free to wrap it into a repository pattern or any way you prefer.&lt;/p&gt;
&lt;p&gt;Create a static class &lt;code&gt;DbContextExtensions&lt;/code&gt; and implement a generic extension that will receive an entity and the state to be tracked, returning an updated entity.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;DbContextExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TEntity&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;SaveEntityStateAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TEntity&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; DbContext context,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        TEntity entity,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        EntityState state,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TEntity : Entity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (context == &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ArgumentNullException(&lt;span class=&#34;keyword&#34;&gt;nameof&lt;/span&gt;(context));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (entity == &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ArgumentNullException(&lt;span class=&#34;keyword&#34;&gt;nameof&lt;/span&gt;(entity));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; entry = context.Entry(entity);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        entry.State = state;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; context.SaveChangesAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        entry.State = EntityState.Detached;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; entry.Entity;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The method creates an &lt;code&gt;EntityEntry&amp;lt;TEntity&amp;gt;&lt;/code&gt;, changes the state to the one provided and immediately requests to flush changes. Because the context is now tracking the entity, it will execute a database instruction based on the entry state (&lt;code&gt;Added|Modified|Deleted&lt;/code&gt;). Then, it detaches the entity right before returning to prevent &lt;em&gt;Identity Resolution&lt;/em&gt; exceptions on future mutations and returns the entity with the most recent values (usefull to get values generated by the database, like an identity column).&lt;/p&gt;
&lt;p&gt;Open the &lt;code&gt;ProgramHost&lt;/code&gt; class and create some test code using the extension method and the immutable &lt;code&gt;PersonEntity&lt;/code&gt;. In this case I’m creating &lt;em&gt;Bruce Wayne&lt;/em&gt; and then updating both birthdate and middle name.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProgramHost&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IHostedService&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; ILogger&amp;lt;ProgramHost&amp;gt; _logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; SampleDbContext _context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProgramHost&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        ILogger&amp;lt;ProgramHost&amp;gt; logger,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        SampleDbContext context&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger = logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _context = context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;StartAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Setting database to a clean state&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.EnsureDeletedAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.EnsureCreatedAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Creating an explicit database transaction&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; tx = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.BeginTransactionAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Adding person&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; person = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.SaveEntityStateAsync(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; PersonEntity(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Forename: &lt;span class=&#34;string&#34;&gt;&amp;quot;Bruce&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Surname: &lt;span class=&#34;string&#34;&gt;&amp;quot;Wayne&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ), EntityState.Added, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Updating person&amp;#x27;s birthdate&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.SaveEntityStateAsync(person &lt;span class=&#34;keyword&#34;&gt;with&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            MiddleNames = &lt;span class=&#34;string&#34;&gt;&amp;quot;Thomas&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Birthdate = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; DateOnly(&lt;span class=&#34;number&#34;&gt;1972&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;02&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;19&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, EntityState.Modified, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Commiting database transaction&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; tx.CommitAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;StopAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct&lt;/span&gt;)&lt;/span&gt; =&amp;gt; Task.CompletedTask;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you execute the code, you’ll see database instructions being sent to SQLite by Entity Framework Core to insert and then update the entity.&lt;/p&gt;
&lt;img src=&#34;/2022/08/13/Immutability-and-Entity-Framework-Core/05_logs_entity_mutate.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Open the SQLite file and you’ll see &lt;em&gt;Bruce Wayne&lt;/em&gt; was both created and updated with a birthdate and middle name.&lt;/p&gt;
&lt;img src=&#34;/2022/08/13/Immutability-and-Entity-Framework-Core/06_db_persons_table.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Certainly you don’t want to be writing &lt;code&gt;EntityState.Added|Updated|Deleted&lt;/code&gt; every time an entity needs to be manipulated, so lets create dedicated extensions for each operation and update your test code.&lt;/p&gt;
&lt;p&gt;Change the method to &lt;code&gt;private&lt;/code&gt; and implement a &lt;code&gt;CreateAsync&lt;/code&gt;, &lt;code&gt;UpdateAsync&lt;/code&gt; and &lt;code&gt;DeleteAsync&lt;/code&gt; extension methods that will reuse the existing one.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;DbContextExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TEntity&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;CreateAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TEntity&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; DbContext context,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        TEntity entity,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TEntity : Entity&lt;/span&gt; =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; context.SaveEntityStateAsync(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        entity,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        EntityState.Added,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ct&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TEntity&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;UpdateAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TEntity&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; DbContext context,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        TEntity entity,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TEntity : Entity&lt;/span&gt; =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; context.SaveEntityStateAsync(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        entity,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        EntityState.Modified,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ct&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;DeleteAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TEntity&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; DbContext context,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        TEntity entity,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TEntity : Entity&lt;/span&gt; =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; context.SaveEntityStateAsync(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        entity,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        EntityState.Deleted,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ct&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TEntity&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;SaveEntityStateAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TEntity&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; DbContext context,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        TEntity entity,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        EntityState state,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TEntity : Entity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (context == &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ArgumentNullException(&lt;span class=&#34;keyword&#34;&gt;nameof&lt;/span&gt;(context));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (entity == &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ArgumentNullException(&lt;span class=&#34;keyword&#34;&gt;nameof&lt;/span&gt;(entity));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; entry = context.Entry(entity);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        entry.State = state;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; context.SaveChangesAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        entry.State = EntityState.Detached;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; entry.Entity;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Use the new methods in our &lt;code&gt;ProgramHost&lt;/code&gt; class.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProgramHost&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IHostedService&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; ILogger&amp;lt;ProgramHost&amp;gt; _logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; SampleDbContext _context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProgramHost&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        ILogger&amp;lt;ProgramHost&amp;gt; logger,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        SampleDbContext context&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger = logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _context = context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;StartAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Setting database to a clean state&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.EnsureDeletedAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.EnsureCreatedAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Creating an explicit database transaction&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; tx = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.BeginTransactionAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Adding person&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; person = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.CreateAsync(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; PersonEntity(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Forename: &lt;span class=&#34;string&#34;&gt;&amp;quot;Bruce&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Surname: &lt;span class=&#34;string&#34;&gt;&amp;quot;Wayne&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ), ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Updating person&amp;#x27;s birthdate&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.UpdateAsync(person &lt;span class=&#34;keyword&#34;&gt;with&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            MiddleNames = &lt;span class=&#34;string&#34;&gt;&amp;quot;Thomas&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Birthdate = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; DateOnly(&lt;span class=&#34;number&#34;&gt;1972&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;02&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;19&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger.LogDebug(&lt;span class=&#34;string&#34;&gt;&amp;quot;Commiting database transaction&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; tx.CommitAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;StopAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct&lt;/span&gt;)&lt;/span&gt; =&amp;gt; Task.CompletedTask;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;I hope this article gave you a good idea on how to use Entity Framework Core to abstract your application from the database while still implementing the immutable design pattern to manage application state.&lt;/p&gt;
&lt;p&gt;There are both advantages and disadvantages of using immutable entities over mutable ones, after all no size fits all, but I think its nice to know you have the option to use it without sacrificing productivity by having to implement database access yourself. As long you ensure &lt;em&gt;Change Tracking&lt;/em&gt; is disabled for all queries and every code uses your abstractions to create, update or delete entities, everything will work just fine.&lt;/p&gt;
&lt;p&gt;Just a small note about explicit database transactions. If you are implementing an ASP.NET Core application and using the mediator pattern, some time ago I created a bunch of articles and one of them provided an approach to &lt;a href=&#34;/2020/11/07/Transaction-Management-With-Mediator-Pipelines-in-ASP-NET-Core/&#34; title=&#34;Transaction Management With Mediator Pipelines in ASP.NET Core&#34;&gt;manage Entity Framework Core transactions globally using a pipeline&lt;/a&gt;. I use that approach all the time, you may find it helpful too.&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="csharp" />
        <category term="patterns" />
        <category term="efcore" />
        <updated>2022-08-12T23:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2021/03/09/Integrating-Dapper-with-Entity-Framework-Core/</id>
        <title>Integrating Dapper with Entity Framework Core</title>
        <link rel="alternate" href="https://code-corner.dev/2021/03/09/Integrating-Dapper-with-Entity-Framework-Core/"/>
        <content type="html">&lt;p&gt;Nowadays it is extremely rare to implement an application without using any sort of library for &lt;em&gt;Object-Relational Mapping (ORM)&lt;/em&gt; to reduce development time by removing the need to implement a lot of boilerplate code to access a database. In the .NET world that usually means using &lt;a href=&#34;https://docs.microsoft.com/en-us/ef/core/&#34;&gt;Entity Framework Core&lt;/a&gt; or &lt;a href=&#34;https://nhibernate.info/&#34;&gt;NHibernate&lt;/a&gt; both offering strong tooling for CRUD operations, data type conversions, strong typed queries using LINQ with &lt;code&gt;IQueryable&lt;/code&gt; and so on.&lt;/p&gt;
&lt;p&gt;Despite the pros of using an ORM there are also some cons that, while may not prevent them to be widely used inside an application, they may need to be replaced in some areas either for performance reasons or limitations. This usually means working directly with &lt;a href=&#34;https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/ado-net-overview&#34;&gt;ADO.NET&lt;/a&gt; (oh hell no!) or use Micro ORM libraries, like &lt;a href=&#34;https://github.com/StackExchange/Dapper&#34;&gt;Dapper&lt;/a&gt;, that are focused on performance and providing a simpler way to map database queries into objects.&lt;/p&gt;
&lt;p&gt;In this article I’m going to demonstrate how Dapper can easily be integrated with Entity Framework Core (and probably with any other ORM) without using &lt;code&gt;TransactionScope&lt;/code&gt;, while trying to keep the same contracts via extension methods to &lt;code&gt;DbContext&lt;/code&gt; instances and ensuring the SQL is properly logged in a similar way to what EF Core usually does.&lt;/p&gt;
&lt;h1 id=&#34;Dapper-Requirements&#34;&gt;&lt;a href=&#34;#Dapper-Requirements&#34; class=&#34;headerlink&#34; title=&#34;Dapper Requirements&#34;&gt;&lt;/a&gt;Dapper Requirements&lt;/h1&gt;&lt;p&gt;To work with Dapper, the only requirements are a &lt;code&gt;DbConnection&lt;/code&gt;, the SQL text, and some optional parameters, like a &lt;code&gt;DbTransaction&lt;/code&gt;, command timeout, query parameters and so on. Sometimes is is also necessary to globally register some custom &lt;code&gt;TypeHandler&amp;lt;T&amp;gt;&lt;/code&gt; for the when it can’t convert a given database type to its CLR representation.&lt;/p&gt;
&lt;p&gt;Assuming we want to execute a simple &lt;code&gt;SELECT @SomeParameter&lt;/code&gt; statement via Dapper, what code we must implement to get everything we need from a &lt;code&gt;DbContext&lt;/code&gt;?&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// get the underline DbConnection&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; connection = context.Database.GetDbConnection();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// get the underline DbTransaction, if any&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; transaction = context.Database.CurrentTransaction?.GetDbTransaction();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// get the currently configured command timeout&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; commandTimeout = context.Database.GetCommandTimeout();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// create a Dapper CommandDefinition&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; command = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CommandDefinition(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;string&#34;&gt;&amp;quot;SELECT @SomeParameter&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        SomeParameter = &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    transaction,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    commandTimeout,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    cancellationToken: ct&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; _ = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; connection.QueryAsync&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt;(command);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As shown, the database facade has everything we need to make Dapper work but there is a caveat that must be properly addressed:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dapper always executes the SQL immediately into the database&lt;/strong&gt;, which means it won’t detect changes made on tracked entities before &lt;code&gt;SaveChanges&lt;/code&gt; is invoked and it won’t wait to flush changes either, so be very careful when managing database access.&lt;/p&gt;
&lt;p&gt;My recommendation is to always open a transaction explicitly via &lt;code&gt;context.Database.BeginTransactionAsync&lt;/code&gt; before running any mutation.&lt;/p&gt;
&lt;p&gt;If you are using the mediator pattern, I recommend my previous article that explains &lt;a href=&#34;/2020/11/07/Transaction-Management-With-Mediator-Pipelines-in-ASP-NET-Core/&#34; title=&#34;Transaction Management With Mediator Pipelines in ASP.NET Core&#34;&gt;how to create a mediator pipeline that enforces database transactions when handling commands&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With all of this in mind, lets implement an example project that showcases what we just talked about.&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id=&#34;The-project&#34;&gt;&lt;a href=&#34;#The-project&#34; class=&#34;headerlink&#34; title=&#34;The project&#34;&gt;&lt;/a&gt;The project&lt;/h1&gt;&lt;p&gt;The source code for this article can be found on &lt;a href=&#34;https://github.com/gravity00/EntityFrameworkCoreWithDapper&#34;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Start by opening Visual Studio and creating an &lt;strong&gt;ASP.NET Core Web Application&lt;/strong&gt; with a name and a location at your preference.&lt;/p&gt;
&lt;img src=&#34;/2021/03/09/Integrating-Dapper-with-Entity-Framework-Core/01_project_create.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Choose an &lt;strong&gt;empty project&lt;/strong&gt; since this is just a demo and we are going to setup only the required dependencies.&lt;/p&gt;
&lt;img src=&#34;/2021/03/09/Integrating-Dapper-with-Entity-Framework-Core/02_project_create_aspnet.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Install the Nuget &lt;code&gt;Swashbuckle.AspNetCore&lt;/code&gt;:&lt;/p&gt;
&lt;img src=&#34;/2021/03/09/Integrating-Dapper-with-Entity-Framework-Core/03_nuget_swagger.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Open the &lt;code&gt;Startup.cs&lt;/code&gt; file and configure both MVC and Swagger so we can use its UI to test our endpoints more easily.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Startup&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ConfigureServices&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IServiceCollection services&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddMvc();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddSwaggerGen();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Configure&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IApplicationBuilder app&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.UseDeveloperExceptionPage();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.UseSwagger();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.UseSwaggerUI(c =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            c.SwaggerEndpoint(&lt;span class=&#34;string&#34;&gt;&amp;quot;/swagger/v1/swagger.json&amp;quot;&lt;/span&gt;, &lt;span class=&#34;string&#34;&gt;&amp;quot;EF Core with Dapper Example Api V1&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.UseRouting();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.UseEndpoints(endpoints =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            endpoints.MapControllers();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;The-Web-API&#34;&gt;&lt;a href=&#34;#The-Web-API&#34; class=&#34;headerlink&#34; title=&#34;The Web API&#34;&gt;&lt;/a&gt;The Web API&lt;/h1&gt;&lt;p&gt;Since the objective of this article is to show how Dapper can be integrated with Entity Framework Core, we are going to create a simple endpoint to manage products:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GET &amp;#x2F;products — lists products, including their current price and last date when it has changed;&lt;/li&gt;
&lt;li&gt;POST &amp;#x2F;products — creates a product with a given price;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;The-Database-Model&#34;&gt;&lt;a href=&#34;#The-Database-Model&#34; class=&#34;headerlink&#34; title=&#34;The Database Model&#34;&gt;&lt;/a&gt;The Database Model&lt;/h2&gt;&lt;p&gt;We need a SQL database to run our queries so, to simplify our setup, we are going to use the SQLite provider for Entity Framework Core and configure it as an in-memory instance.&lt;/p&gt;
&lt;p&gt;Install the Nuget &lt;code&gt;Microsoft.EntityFrameworkCore.Sqlite&lt;/code&gt;:&lt;/p&gt;
&lt;img src=&#34;/2021/03/09/Integrating-Dapper-with-Entity-Framework-Core/04_nuget_efcore_sqlite.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Create a &lt;code&gt;Database&lt;/code&gt; folder and inside create entities for products and price history, both mapped into an Entity Framework context:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ApiDbContext&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;DbContext&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ApiDbContext&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;DbContextOptions&amp;lt;ApiDbContext&amp;gt; options&lt;/span&gt;) : &lt;span class=&#34;title&#34;&gt;base&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;options&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;OnModelCreating&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;ModelBuilder builder&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;base&lt;/span&gt;.OnModelCreating(builder);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        builder.Entity&amp;lt;ProductEntity&amp;gt;(cfg =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.ToTable(&lt;span class=&#34;string&#34;&gt;&amp;quot;Product&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasKey(e =&amp;gt; e.Id);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasAlternateKey(e =&amp;gt; e.ExternalId);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasIndex(e =&amp;gt; e.Code).IsUnique();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Id)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .ValueGeneratedOnAdd();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.ExternalId)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Code)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .HasMaxLength(&lt;span class=&#34;number&#34;&gt;8&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Name)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .HasMaxLength(&lt;span class=&#34;number&#34;&gt;128&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        builder.Entity&amp;lt;PriceHistoryEntity&amp;gt;(cfg =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.ToTable(&lt;span class=&#34;string&#34;&gt;&amp;quot;PriceHistory&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasKey(e =&amp;gt; e.Id);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasIndex(e =&amp;gt; e.CreatedOn);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Id)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .ValueGeneratedOnAdd();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasOne(e =&amp;gt; e.Product)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .WithMany(p =&amp;gt; p.PricesHistory)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Price)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.CreatedOn)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProductEntity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; Id &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Guid ExternalId &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Code &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Name &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;virtual&lt;/span&gt; ICollection&amp;lt;PriceHistoryEntity&amp;gt; PricesHistory &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProductEntity&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        PricesHistory = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; HashSet&amp;lt;PriceHistoryEntity&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;PriceHistoryEntity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; Id &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;virtual&lt;/span&gt; ProductEntity Product &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;decimal&lt;/span&gt; Price &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; DateTime CreatedOn &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Open the &lt;code&gt;Startup.cs&lt;/code&gt; file and add the database context to the container. Because the database is stored in-memory, we must ensure all tables are created when the application starts and at least one connection is always open so the SQLite provider won’t discard it from memory:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Startup&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; ConnectionString = &lt;span class=&#34;string&#34;&gt;&amp;quot;Data Source=EntityFrameworkCoreWithDapper;Mode=Memory;Cache=Shared&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; SqliteConnection _keepAliveConnection;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ConfigureServices&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IServiceCollection services&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddDbContext&amp;lt;ApiDbContext&amp;gt;(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.UseSqlite(ConnectionString);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Configure&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IApplicationBuilder app&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// this ensures at least one connection is open and the database is kept in-memory while the application is running&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _keepAliveConnection = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; SqliteConnection(ConnectionString);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        keepAliveConnection.Open();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; scope = app.ApplicationServices.GetRequiredService&amp;lt;IServiceScopeFactory&amp;gt;().CreateScope())&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; ctx = scope.ServiceProvider.GetRequiredService&amp;lt;ApiDbContext&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ctx.Database.EnsureCreated();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;The-Products-Controller&#34;&gt;&lt;a href=&#34;#The-Products-Controller&#34; class=&#34;headerlink&#34; title=&#34;The Products Controller&#34;&gt;&lt;/a&gt;The Products Controller&lt;/h2&gt;&lt;p&gt;Now that we have configured the database, create a &lt;code&gt;Controllers&lt;/code&gt; folder, the &lt;code&gt;ProductsController&lt;/code&gt; class and its models. For now, we are going to implement our logic using only the Entity Framework context.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;Route(&lt;span class=&#34;string&#34;&gt;&amp;quot;products&amp;quot;&lt;/span&gt;)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProductsController&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;ControllerBase&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; ApiDbContext _context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProductsController&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;ApiDbContext context&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _context = context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;HttpGet&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;ProductModel&lt;/span&gt;&amp;gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;GetAllAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;[FromQuery] &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;? skip, [FromQuery] &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;? take, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; (&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;from&lt;/span&gt; p &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; _context.Set&amp;lt;ProductEntity&amp;gt;()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;select&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    Id = p.ExternalId,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    p.Code,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    p.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    MostRecentPriceHistory = p&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        .PricesHistory&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        .OrderByDescending(ph =&amp;gt; ph.CreatedOn)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        .First()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            )&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .OrderBy(p =&amp;gt; p.Code)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .Skip(skip ?? &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .Take(take ?? &lt;span class=&#34;number&#34;&gt;20&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .Select(p =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ProductModel&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Id = p.Id,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Code = p.Code,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Name = p.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Price = p.MostRecentPriceHistory.Price,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                PriceChangedOn = p.MostRecentPriceHistory.CreatedOn&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .ToListAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;HttpPost&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task&amp;lt;CreateProductResultModel&amp;gt; &lt;span class=&#34;title&#34;&gt;CreateAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;[FromBody] CreateProductModel model, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; externalId = Guid.NewGuid();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; tx = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.BeginTransactionAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; product = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ProductEntity&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ExternalId = externalId,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Code = model.Code,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Name = model.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            PricesHistory =&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; PriceHistoryEntity&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    Price = model.Price,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    CreatedOn = DateTime.UtcNow&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Set&amp;lt;ProductEntity&amp;gt;().AddAsync(product, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.SaveChangesAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; tx.CommitAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CreateProductResultModel&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Id = externalId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CreateProductModel&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Code &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Name &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;decimal&lt;/span&gt; Price &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; DateTime PriceChangedOn &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CreateProductResultModel&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Guid Id &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Right now, the project content should look as follows:&lt;/p&gt;
&lt;img src=&#34;/2021/03/09/Integrating-Dapper-with-Entity-Framework-Core/05_project_structure.png&#34; class=&#34;&#34;&gt;

&lt;h1 id=&#34;Dapper-Integration&#34;&gt;&lt;a href=&#34;#Dapper-Integration&#34; class=&#34;headerlink&#34; title=&#34;Dapper Integration&#34;&gt;&lt;/a&gt;Dapper Integration&lt;/h1&gt;&lt;p&gt;Now that we have a running API that manages products and their prices using Entity Framework Core, we can now integrate Dapper into the solution and apply what we learned at the start of this article.&lt;/p&gt;
&lt;p&gt;Install the Nuget &lt;code&gt;Dapper&lt;/code&gt;:&lt;/p&gt;
&lt;img src=&#34;/2021/03/09/Integrating-Dapper-with-Entity-Framework-Core/06_nuget_dapper.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Since Dapper uses extension methods over &lt;code&gt;IDbConnection&lt;/code&gt; and we can extract everything needed from a &lt;code&gt;DbContext&lt;/code&gt; instance (even an &lt;code&gt;ILogger&lt;/code&gt; to log our raw SQL), lets keep that philosophy and replicate those extension methods but this time to an Entity Framework Core context.&lt;/p&gt;
&lt;p&gt;Inside the &lt;code&gt;Database&lt;/code&gt; folder create a static &lt;code&gt;DapperDbContextExtensions&lt;/code&gt; class, that will containing all the extension methods, and a &lt;code&gt;DapperEFCoreCommand&lt;/code&gt; structure, used to wrap both logging and Dapper’s &lt;code&gt;CommandDefinition&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For demo purposes we are only going to expose methods to query a collection of items and to execute commands but feel free to add your owns (like &lt;code&gt;FirstAsync&lt;/code&gt;). I also put the &lt;code&gt;CancellationToken&lt;/code&gt; at the start since all other parameters are optional and I have my fair share of cancellation tokens being passed as the &lt;code&gt;object parameters&lt;/code&gt;, but change them in a way that makes more sense to you.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;95&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;DapperDbContextExtensions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;QueryAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; DbContext context,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; text,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; parameters = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;? timeout = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CommandType? type = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; command = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; DapperEFCoreCommand(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            context,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            text,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            parameters,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            timeout,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            type,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ct&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; connection = context.Database.GetDbConnection();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; connection.QueryAsync&amp;lt;T&amp;gt;(command.Definition);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task&amp;lt;&lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;ExecuteAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt; DbContext context,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; text,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; parameters = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;? timeout = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CommandType? type = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; command = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; DapperEFCoreCommand(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            context,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            text,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            parameters,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            timeout,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            type,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ct&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; connection = context.Database.GetDbConnection();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; connection.ExecuteAsync(command.Definition);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;struct&lt;/span&gt; DapperEFCoreCommand : IDisposable&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; ILogger&amp;lt;DapperEFCoreCommand&amp;gt; _logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;DapperEFCoreCommand&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        DbContext context,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; text,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; parameters,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;? timeout,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CommandType? type,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger = context.GetService&amp;lt;ILogger&amp;lt;DapperEFCoreCommand&amp;gt;&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; transaction = context.Database.CurrentTransaction?.GetDbTransaction();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; commandType = type ?? CommandType.Text;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; commandTimeout = timeout ?? context.Database.GetCommandTimeout() ?? &lt;span class=&#34;number&#34;&gt;30&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Definition = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CommandDefinition(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            text,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            parameters,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            transaction,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            commandTimeout,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            commandType,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cancellationToken: ct&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_logger.IsEnabled(LogLevel.Debug))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _logger.LogDebug(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;string&#34;&gt;@&amp;quot;Executing DbCommand [CommandType=&amp;#x27;&amp;#123;commandType&amp;#125;&amp;#x27;, CommandTimeout=&amp;#x27;&amp;#123;commandTimeout&amp;#125;&amp;#x27;]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;&amp;#123;commandText&amp;#125;&amp;quot;&lt;/span&gt;, Definition.CommandType, Definition.CommandTimeout, Definition.CommandText);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; CommandDefinition Definition &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Dispose&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_logger.IsEnabled(LogLevel.Information))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _logger.LogInformation(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;string&#34;&gt;@&amp;quot;Executed DbCommand [CommandType=&amp;#x27;&amp;#123;commandType&amp;#125;&amp;#x27;, CommandTimeout=&amp;#x27;&amp;#123;commandTimeout&amp;#125;&amp;#x27;]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;&amp;#123;commandText&amp;#125;&amp;quot;&lt;/span&gt;, Definition.CommandType, Definition.CommandTimeout, Definition.CommandText);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Since we are using &lt;code&gt;Guid&lt;/code&gt; properties in our models but they are stored as &lt;code&gt;TEXT&lt;/code&gt; and Dapper doesn’t know how to do the conversion when reading from a SQLite database, we also need to add a global type handler.&lt;/p&gt;
&lt;p&gt;Open the &lt;code&gt;Startup.cs&lt;/code&gt; file, create an inner class &lt;code&gt;GuidTypeHandler&lt;/code&gt;, that will parse the string into a &lt;code&gt;Guid&lt;/code&gt;, and register the handler on application startup:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Startup&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ConfigureServices&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IServiceCollection services&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        SqlMapper.AddTypeHandler(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; GuidTypeHandler());&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddDbContext&amp;lt;ApiDbContext&amp;gt;(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.UseSqlite(ConnectionString);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;).AddTransient&amp;lt;ApiDbSqlRunner&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;GuidTypeHandler&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;SqlMapper.TypeHandler&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;Guid&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SetValue&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IDbDataParameter parameter, Guid &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt; =&amp;gt; parameter.Value = &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; Guid &lt;span class=&#34;title&#34;&gt;Parse&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt; =&amp;gt; Guid.Parse((&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Optimizations-with-Dapper&#34;&gt;&lt;a href=&#34;#Optimizations-with-Dapper&#34; class=&#34;headerlink&#34; title=&#34;Optimizations with Dapper&#34;&gt;&lt;/a&gt;Optimizations with Dapper&lt;/h1&gt;&lt;p&gt;As stated before, Entity Framework will usually make it easier to access the database and remove some of the boilerplate code but, like most advanced frameworks, it has some drawbacks, specially performance degradation that sometimes can’t be ignored in critical paths.&lt;/p&gt;
&lt;p&gt;Lets analyse each endpoint and see if we can improve both the generated SQL and the total of database interactions.&lt;/p&gt;
&lt;h2 id=&#34;POST-products&#34;&gt;&lt;a href=&#34;#POST-products&#34; class=&#34;headerlink&#34; title=&#34;POST &amp;#x2F;products&#34;&gt;&lt;/a&gt;POST &amp;#x2F;products&lt;/h2&gt;&lt;p&gt;This endpoint is responsible to create a product with a given initial price. Because prices are stored in a history table, being the most recent entry the current product price, this action has to insert both a line in the products and price history tables.&lt;/p&gt;
&lt;p&gt;If we look at the logs we can see two commands being executed by the Entity Framework with a total of four operations:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;info: Microsoft.EntityFrameworkCore.Database.Command[20101]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      Executed DbCommand (6ms) [Parameters=[@p0=&amp;#x27;?&amp;#x27; (Size = 8), @p1=&amp;#x27;?&amp;#x27;, @p2=&amp;#x27;?&amp;#x27; (Size = 16)], CommandType=&amp;#x27;Text&amp;#x27;, CommandTimeout=&amp;#x27;30&amp;#x27;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      INSERT INTO &amp;quot;Product&amp;quot; (&amp;quot;Code&amp;quot;, &amp;quot;ExternalId&amp;quot;, &amp;quot;Name&amp;quot;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      VALUES (@p0, @p1, @p2);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      SELECT &amp;quot;Id&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      FROM &amp;quot;Product&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      WHERE changes() = 1 AND &amp;quot;rowid&amp;quot; = last_insert_rowid();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;info: Microsoft.EntityFrameworkCore.Database.Command[20101]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      Executed DbCommand (1ms) [Parameters=[@p3=&amp;#x27;?&amp;#x27;, @p4=&amp;#x27;?&amp;#x27;, @p5=&amp;#x27;?&amp;#x27;], CommandType=&amp;#x27;Text&amp;#x27;, CommandTimeout=&amp;#x27;30&amp;#x27;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      INSERT INTO &amp;quot;PriceHistory&amp;quot; (&amp;quot;CreatedOn&amp;quot;, &amp;quot;Price&amp;quot;, &amp;quot;ProductId&amp;quot;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      VALUES (@p3, @p4, @p5);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      SELECT &amp;quot;Id&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      FROM &amp;quot;PriceHistory&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      WHERE changes() = 1 AND &amp;quot;rowid&amp;quot; = last_insert_rowid();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;This happens because Entity Framework and the SQLite provider don’t know what our code needs from each entity after an insert, so the only option is to execute a command that does the insert, selects database generated columns, and update properties of the tracked instances, in this case, the primary keys.&lt;/p&gt;
&lt;p&gt;Since we know our code doesn’t need anything from the database, because we are only returning the product external id that was calculated inside the controller, we can execute a single SQL statement containing both inserts:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;INSERT INTO Product (ExternalId, Code, Name)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;VALUES (@ExternalId, @Code, @Name);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;INSERT INTO PriceHistory (Price, CreatedOn, ProductId)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;SELECT @Price, @CreatedOn, Id&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;FROM Product&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;WHERE&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    rowid = last_insert_rowid();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Change the &lt;code&gt;CreateAsync&lt;/code&gt; action to use the extension method &lt;code&gt;ExecuteAsync&lt;/code&gt; with this statement, passing the arguments. Keep in mind that SQLite is case sensitive when parsing the parameter name, so you must ensure the anonymous object property names match with the ones inside the statement:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;Route(&lt;span class=&#34;string&#34;&gt;&amp;quot;products&amp;quot;&lt;/span&gt;)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProductsController&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;ControllerBase&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;HttpPost&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task&amp;lt;CreateProductResultModel&amp;gt; &lt;span class=&#34;title&#34;&gt;CreateAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;[FromBody] CreateProductModel model, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; externalId = Guid.NewGuid();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; tx = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.BeginTransactionAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.ExecuteAsync(ct, &lt;span class=&#34;string&#34;&gt;@&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;INSERT INTO Product (ExternalId, Code, Name)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;VALUES (@ExternalId, @Code, @Name);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;INSERT INTO PriceHistory (Price, CreatedOn, ProductId)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;SELECT @Price, @CreatedOn, Id&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;FROM Product&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;WHERE&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;    rowid = last_insert_rowid();&amp;quot;&lt;/span&gt;, &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ExternalId = externalId,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            model.Code,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            model.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            model.Price,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedOn = DateTime.UtcNow&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; tx.CommitAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CreateProductResultModel&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Id = externalId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;Remember that, even if we are executing a single command in the database, it contains two instructions so it still must be wrapped by an explicit database transaction.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When creating a new product using the Swagger UI endpoint (&lt;a href=&#34;https://localhost:44310/swagger/index.html&#34;&gt;https://localhost:44310/swagger/index.html&lt;/a&gt;), if you look at your Visual Studio output console, a log similar to the following should appear showing the custom SQL:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dbug: EntityFrameworkCoreWithDapper.Database.DapperEFCoreCommand[0]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      Executing DbCommand [CommandType=&amp;#x27;Text&amp;#x27;, CommandTimeout=&amp;#x27;30&amp;#x27;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      INSERT INTO Product (ExternalId, Code, Name)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      VALUES (@ExternalId, @Code, @Name);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      INSERT INTO PriceHistory (Price, CreatedOn, ProductId)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      SELECT @Price, @CreatedOn, Id&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      FROM Product&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      WHERE&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          rowid = last_insert_rowid();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;info: EntityFrameworkCoreWithDapper.Database.DapperEFCoreCommand[0]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      Executed DbCommand [CommandType=&amp;#x27;Text&amp;#x27;, CommandTimeout=&amp;#x27;30&amp;#x27;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      INSERT INTO Product (ExternalId, Code, Name)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      VALUES (@ExternalId, @Code, @Name);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      INSERT INTO PriceHistory (Price, CreatedOn, ProductId)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      SELECT @Price, @CreatedOn, Id&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      FROM Product&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      WHERE&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          rowid = last_insert_rowid();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;GET-products&#34;&gt;&lt;a href=&#34;#GET-products&#34; class=&#34;headerlink&#34; title=&#34;GET &amp;#x2F;products&#34;&gt;&lt;/a&gt;GET &amp;#x2F;products&lt;/h2&gt;&lt;p&gt;This endpoint is responsible for returning a paginated collections of products with their current price and the timestamp when it was last updated, ordered by product code.&lt;/p&gt;
&lt;p&gt;Lets extract the SQL statement generated by the Entity Framework Core from the logs:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;info: Microsoft.EntityFrameworkCore.Database.Command[20101]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      Executed DbCommand (3ms) [Parameters=[@__p_1=&amp;#x27;?&amp;#x27;, @__p_0=&amp;#x27;?&amp;#x27;], CommandType=&amp;#x27;Text&amp;#x27;, CommandTimeout=&amp;#x27;30&amp;#x27;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      SELECT &amp;quot;p1&amp;quot;.&amp;quot;ExternalId&amp;quot; AS &amp;quot;Id&amp;quot;, &amp;quot;p1&amp;quot;.&amp;quot;Code&amp;quot;, &amp;quot;p1&amp;quot;.&amp;quot;Name&amp;quot;, (&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          SELECT &amp;quot;p&amp;quot;.&amp;quot;Price&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          FROM &amp;quot;PriceHistory&amp;quot; AS &amp;quot;p&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          WHERE &amp;quot;p1&amp;quot;.&amp;quot;Id&amp;quot; = &amp;quot;p&amp;quot;.&amp;quot;ProductId&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          ORDER BY &amp;quot;p&amp;quot;.&amp;quot;CreatedOn&amp;quot; DESC&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          LIMIT 1) AS &amp;quot;Price&amp;quot;, (&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          SELECT &amp;quot;p0&amp;quot;.&amp;quot;CreatedOn&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          FROM &amp;quot;PriceHistory&amp;quot; AS &amp;quot;p0&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          WHERE &amp;quot;p1&amp;quot;.&amp;quot;Id&amp;quot; = &amp;quot;p0&amp;quot;.&amp;quot;ProductId&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          ORDER BY &amp;quot;p0&amp;quot;.&amp;quot;CreatedOn&amp;quot; DESC&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          LIMIT 1) AS &amp;quot;PriceChangedOn&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      FROM &amp;quot;Product&amp;quot; AS &amp;quot;p1&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      ORDER BY &amp;quot;p1&amp;quot;.&amp;quot;Code&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      LIMIT @__p_1 OFFSET @__p_0&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As we can see, because we need both the Price and CreatedOn columns from the most recent price history entry, the SQLite provider decided to create two sub-queries. The database engine is relatively smart to know how to optimize them but lets ensure the engine optimizes the access as follows:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1. order products by code&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2. filter products by skip and take&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3. join with latest price history entry, grouped by product, using the ROWID for fast access&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;SELECT p.ExternalId as Id, p.Code, p.Name, lph.Price, lph.CreatedOn as PriceChangedOn&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;FROM (&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    SELECT Id, ExternalId, Code, Name, RowId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    FROM Product&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ORDER BY Code DESC&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    LIMIT @Take OFFSET @Skip&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;) p&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;INNER JOIN (&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    SELECT ph.ProductId, ph.Price, ph.CreatedOn&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    FROM PriceHistory ph&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    INNER JOIN (&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        SELECT MAX(RowId) RowId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        FROM PriceHistory&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        GROUP BY ProductId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ) phLatest ON ph.RowId = phLatest.RowId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;) lph ON p.Id = lph.ProductId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;Note: this may not be the most optimized access but remember, this is for demo purposes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Change the &lt;code&gt;GetAllAsync&lt;/code&gt; action to use the &lt;code&gt;QueryAsync&amp;lt;T&amp;gt;&lt;/code&gt; extension method, passing this SQL and both the skip and take as arguments.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;Route(&lt;span class=&#34;string&#34;&gt;&amp;quot;products&amp;quot;&lt;/span&gt;)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProductsController&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;ControllerBase&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;HttpGet&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;ProductModel&lt;/span&gt;&amp;gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;GetAllAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;[FromQuery] &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;? skip, [FromQuery] &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;? take, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.QueryAsync&amp;lt;ProductModel&amp;gt;(ct, &lt;span class=&#34;string&#34;&gt;@&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;SELECT p.ExternalId as Id, p.Code, p.Name, lph.Price, lph.CreatedOn as PriceChangedOn&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;FROM (&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;    SELECT Id, ExternalId, Code, Name, RowId&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;    FROM Product&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;    ORDER BY Code DESC&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;    LIMIT @Take OFFSET @Skip&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;) p&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;INNER JOIN (&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;    SELECT ph.ProductId, ph.Price, ph.CreatedOn&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;    FROM PriceHistory ph&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;    INNER JOIN (&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;        SELECT MAX(RowId) RowId&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;        FROM PriceHistory&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;        GROUP BY ProductId&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;    ) phLatest ON ph.RowId = phLatest.RowId&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;string&#34;&gt;) lph ON p.Id = lph.ProductId&amp;quot;&lt;/span&gt;, &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Skip = skip ?? &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Take = take ?? &lt;span class=&#34;number&#34;&gt;20&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Because we globally registered our &lt;code&gt;GuidTypeHandler&lt;/code&gt;, Dapper will know how to convert the column &lt;code&gt;ExternalId [TEXT]&lt;/code&gt; as a &lt;code&gt;Guid&lt;/code&gt; so we can map our result directly as a &lt;code&gt;ProductModel&lt;/code&gt; type.&lt;/p&gt;
&lt;p&gt;Once again, if you invoke the endpoint using the Swagger UI, the folowing log should be visible:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dbug: EntityFrameworkCoreWithDapper.Database.DapperEFCoreCommand[0]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      Executing DbCommand [CommandType=&amp;#x27;Text&amp;#x27;, CommandTimeout=&amp;#x27;30&amp;#x27;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      SELECT p.ExternalId as Id, p.Code, p.Name, lph.Price, lph.CreatedOn as PriceChangedOn&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      FROM (&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          SELECT Id, ExternalId, Code, Name, RowId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          FROM Product&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          ORDER BY Code DESC&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          LIMIT @Take OFFSET @Skip&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      ) p&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      INNER JOIN (&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          SELECT ph.ProductId, ph.Price, ph.CreatedOn&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          FROM PriceHistory ph&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          INNER JOIN (&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;              SELECT MAX(RowId) RowId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;              FROM PriceHistory&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;              GROUP BY ProductId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          ) phLatest ON ph.RowId = phLatest.RowId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      ) lph ON p.Id = lph.ProductId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;info: EntityFrameworkCoreWithDapper.Database.DapperEFCoreCommand[0]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      Executed DbCommand [CommandType=&amp;#x27;Text&amp;#x27;, CommandTimeout=&amp;#x27;30&amp;#x27;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      SELECT p.ExternalId as Id, p.Code, p.Name, lph.Price, lph.CreatedOn as PriceChangedOn&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      FROM (&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          SELECT Id, ExternalId, Code, Name, RowId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          FROM Product&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          ORDER BY Code DESC&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          LIMIT @Take OFFSET @Skip&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      ) p&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      INNER JOIN (&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          SELECT ph.ProductId, ph.Price, ph.CreatedOn&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          FROM PriceHistory ph&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          INNER JOIN (&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;              SELECT MAX(RowId) RowId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;              FROM PriceHistory&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;              GROUP BY ProductId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          ) phLatest ON ph.RowId = phLatest.RowId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      ) lph ON p.Id = lph.ProductId&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;I hope this article gave you a good idea on how to easily integrate Dapper with Entity Framework Core, either to optimize critical paths or to workaround limitations while removing the need to use &lt;code&gt;TransactionScope&lt;/code&gt;, usually needed for these use cases.&lt;/p&gt;
&lt;p&gt;Remember that I only implemented a few set of operations, but feel free to extend your own, like &lt;code&gt;FirstOrDefaultAsync&lt;/code&gt;, &lt;code&gt;SingleAsync&lt;/code&gt; and even their synchronous operations.&lt;/p&gt;
&lt;p&gt;As an extended note, this approach can also be easily applied to any other ORM, like NHibernate, as long you can access the underline &lt;code&gt;DbConnection&lt;/code&gt; and current &lt;code&gt;DbTransaction&lt;/code&gt; from the context.&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="aspnetcore" />
        <category term="csharp" />
        <category term="efcore" />
        <category term="dapper" />
        <updated>2021-03-09T00:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2020/11/25/Auditing-with-Mediator-Pipelines-in-ASP-NET-Core/</id>
        <title>Auditing with Mediator Pipelines in ASP.NET Core</title>
        <link rel="alternate" href="https://code-corner.dev/2020/11/25/Auditing-with-Mediator-Pipelines-in-ASP-NET-Core/"/>
        <content type="html">&lt;p&gt;When implementing a web application, it can be a good idea to enforce some kind of auditing to all of your client interactions, either to track their behavior over time, to ensure any security breach will be properly logged, or just to help analyze system bugs.&lt;/p&gt;
&lt;p&gt;Previously we talked about using the mediator pattern to implement the &lt;strong&gt;Command Query Responsibility Segregation (CQRS)&lt;/strong&gt; and &lt;strong&gt;Event Sourcing (ES)&lt;/strong&gt; and how pipelines could be used to implement transversal behavior to your application.&lt;/p&gt;
&lt;p&gt;Since commands are responsible to mutate the system state, in this article I’m going to demonstrate how you could implement an audit pipeline to ensure all commands will be stored into a table. Because a variable number of events can be broadcasted when the state changes, the pipeline will also store them into another table and with a reference to the command, ensuring any correlation can be analyzed.&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id=&#34;The-project&#34;&gt;&lt;a href=&#34;#The-project&#34; class=&#34;headerlink&#34; title=&#34;The project&#34;&gt;&lt;/a&gt;The project&lt;/h1&gt;&lt;p&gt;From my previous articles, were I explained how to use the mediator and implement transversal behavior with pipelines, we are going to continue and expand the source code to audit commands and store into the events table anything broadcasted by the mediator without having a specific handler for each event.&lt;/p&gt;
&lt;p&gt;As a reminder, we implemented an endpoint to manage products with the following:&lt;/p&gt;
&lt;p&gt;GET &amp;#x2F;products — search for products using some filters (&lt;code&gt;SearchProductsQuery&lt;/code&gt;);&lt;br&gt;GET &amp;#x2F;products&amp;#x2F;{id} — get a product by its unique identifier (&lt;code&gt;GetProductByIdQuery&lt;/code&gt;);&lt;br&gt;POST &amp;#x2F;products — create a product (&lt;code&gt;CreateProductCommand&lt;/code&gt; and &lt;code&gt;CreatedProductEvent&lt;/code&gt;);&lt;br&gt;PUT &amp;#x2F;products&amp;#x2F;{id} — update a product by its unique identifier (&lt;code&gt;UpdateProductCommand&lt;/code&gt; and &lt;code&gt;UpdatedProductEvent&lt;/code&gt;);&lt;br&gt;DELETE &amp;#x2F;products&amp;#x2F;{id} — delete a product by its unique identifier (&lt;code&gt;DeleteProductCommand&lt;/code&gt; and &lt;code&gt;DeletedProductEvent&lt;/code&gt;);&lt;/p&gt;
&lt;p&gt;You can check them out here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/2020/10/28/Mediator-Pattern-in-ASP-NET-Core-Applications/&#34; title=&#34;Mediator Pattern in ASP.NET Core Applications&#34;&gt;Mediator Pattern in ASP.NET Core Applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/2020/11/02/Using-Mediator-Pipelines-in-ASP-NET-Core-Applications/&#34; title=&#34;Using Mediator Pipelines in ASP.NET Core Applications&#34;&gt;Using Mediator Pipelines in ASP.NET Core Applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/2020/11/04/Validation-with-Mediator-Pipelines-in-ASP-NET-Core-Applications/&#34; title=&#34;Validation with Mediator Pipelines in ASP.NET Core Applications&#34;&gt;Validation with Mediator Pipelines in ASP.NET Core Applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/2020/11/07/Transaction-Management-With-Mediator-Pipelines-in-ASP-NET-Core/&#34; title=&#34;Transaction Management With Mediator Pipelines in ASP.NET Core&#34;&gt;Transaction Management With Mediator Pipelines in ASP.NET Core&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The source code is available on &lt;a href=&#34;https://github.com/gravity00/IntroductionMediatorCQRS&#34;&gt;GitHub&lt;/a&gt;, feel free to give it a look.&lt;/p&gt;
&lt;h1 id=&#34;Auditing&#34;&gt;&lt;a href=&#34;#Auditing&#34; class=&#34;headerlink&#34; title=&#34;Auditing&#34;&gt;&lt;/a&gt;Auditing&lt;/h1&gt;&lt;p&gt;Since in this article we will only audit API actions that mutate state, we are going to intercept commands and store information we find relevant into a specific table:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ExternalId&lt;/strong&gt; — the unique identifier for each command, available via &lt;code&gt;Command.Id&lt;/code&gt; or &lt;code&gt;Command&amp;lt;TResult&amp;gt;.Id&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt; — the command type name from &lt;code&gt;typeof(TCommand).Name&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Payload&lt;/strong&gt; — the command serialized as JSON;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Result&lt;/strong&gt; — if available, the command result serialized as JSON;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CreatedOn&lt;/strong&gt; — date and time when the command was sent into the mediator, available via &lt;code&gt;Command.CreatedOn&lt;/code&gt; or &lt;code&gt;Command&amp;lt;TResult&amp;gt;.CreatedOn&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CreatedBy&lt;/strong&gt; — username from the current request user property, available via &lt;code&gt;Command.CreatedBy&lt;/code&gt; or &lt;code&gt;Command&amp;lt;TResult&amp;gt;.CreatedBy&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ExecutionTime&lt;/strong&gt; — elapsed time the handler spent processing the command;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because events are broadcasted by commands, which are now audited into the database, we are also going to extend the events table and introduce the foreign key &lt;code&gt;CommandId&lt;/code&gt;, referencing the commands.&lt;/p&gt;
&lt;h1 id=&#34;The-Database-Model&#34;&gt;&lt;a href=&#34;#The-Database-Model&#34; class=&#34;headerlink&#34; title=&#34;The Database Model&#34;&gt;&lt;/a&gt;The Database Model&lt;/h1&gt;&lt;p&gt;Inside the &lt;code&gt;Database&lt;/code&gt; folder create a &lt;code&gt;CommandEntity&lt;/code&gt; class and add the new &lt;code&gt;CommandId&lt;/code&gt; property to the existing &lt;code&gt;EventEntity&lt;/code&gt;:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CommandEntity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; Id &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Guid ExternalId &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Name &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Payload &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Result &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; DateTimeOffset CreatedOn &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; CreatedBy &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; TimeSpan ExecutionTime &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;EventEntity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; Id &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; CommandId &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125; &lt;span class=&#34;comment&#34;&gt;// added property&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Guid ExternalId &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Name &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Payload &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; DateTimeOffset CreatedOn &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; CreatedBy &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Open the &lt;code&gt;ApiDbContext&lt;/code&gt; file, configure the mappings for the &lt;code&gt;CommandEntity&lt;/code&gt; and add a required one-to-many relation between this tables:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ApiDbContext&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;DbContext&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;OnModelCreating&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;ModelBuilder builder&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        builder.Entity&amp;lt;CommandEntity&amp;gt;(cfg =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasKey(e =&amp;gt; e.Id);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasAlternateKey(e =&amp;gt; e.ExternalId);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasIndex(e =&amp;gt; e.CreatedOn);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Id)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .ValueGeneratedOnAdd();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.ExternalId)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Name)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .HasMaxLength(&lt;span class=&#34;number&#34;&gt;128&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Payload)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Result);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.CreatedOn)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.CreatedBy)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .HasMaxLength(&lt;span class=&#34;number&#34;&gt;128&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.ExecutionTime)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        builder.Entity&amp;lt;EventEntity&amp;gt;(cfg =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasKey(e =&amp;gt; e.Id);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasAlternateKey(e =&amp;gt; e.ExternalId);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasIndex(e =&amp;gt; e.CreatedOn);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Id)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .ValueGeneratedOnAdd();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.HasOne&amp;lt;CommandEntity&amp;gt;()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .WithMany()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .HasForeignKey(e =&amp;gt; e.CommandId)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.ExternalId)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Name)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .HasMaxLength(&lt;span class=&#34;number&#34;&gt;128&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.Payload)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.CreatedOn)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            cfg.Property(e =&amp;gt; e.CreatedBy)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .IsRequired()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .HasMaxLength(&lt;span class=&#34;number&#34;&gt;128&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;User-Information&#34;&gt;&lt;a href=&#34;#User-Information&#34; class=&#34;headerlink&#34; title=&#34;User Information&#34;&gt;&lt;/a&gt;User Information&lt;/h1&gt;&lt;p&gt;By design, all POCOs provided in this library are immutable and only provide a protected setter for the properties &lt;code&gt;Id&lt;/code&gt;, &lt;code&gt;CreatedOn&lt;/code&gt; and &lt;code&gt;CreatedBy&lt;/code&gt;. This ensures the developer is free to decide either by immutable commands, queries and events, initializing all properties in the constructor, or to expose a public setter instead.&lt;/p&gt;
&lt;p&gt;Since we haven’t made our POCOs immutable, and for demo purposes, we are going to expose a public setter for the &lt;code&gt;CreatedBy&lt;/code&gt; property by implementing our own command, query and event classes.&lt;/p&gt;
&lt;p&gt;Inside the &lt;code&gt;Handlers&lt;/code&gt; folder create a &lt;code&gt;Command.cs&lt;/code&gt;, &lt;code&gt;Query.cs&lt;/code&gt; and &lt;code&gt;Event.cs&lt;/code&gt; files and extend the corresponding &lt;code&gt;Command&lt;/code&gt;, &lt;code&gt;Command&amp;lt;TResult&amp;gt;&lt;/code&gt;, &lt;code&gt;Query&amp;lt;TResult&amp;gt;&lt;/code&gt; and &lt;code&gt;Event&lt;/code&gt; classes, creating a new setter ao getter for &lt;code&gt;CreatedBy&lt;/code&gt;. Since your classes have the same name than the ones provided by &lt;code&gt;Simplesoft.Mediator&lt;/code&gt;, your existing classes will automatically extend from them and expose the new setters without a single change:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Command&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;SimpleSoft.Mediator.Command&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; CreatedBy&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt; =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;base&lt;/span&gt;.CreatedBy;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt; =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;base&lt;/span&gt;.CreatedBy = &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Command&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt; : &lt;span class=&#34;title&#34;&gt;SimpleSoft.Mediator.Command&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; CreatedBy&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt; =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;base&lt;/span&gt;.CreatedBy;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt; =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;base&lt;/span&gt;.CreatedBy = &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Event&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;SimpleSoft.Mediator.Event&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; CreatedBy&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt; =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;base&lt;/span&gt;.CreatedBy;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt; =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;base&lt;/span&gt;.CreatedBy = &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Query&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt; : &lt;span class=&#34;title&#34;&gt;SimpleSoft.Mediator.Query&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; CreatedBy&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt; =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;base&lt;/span&gt;.CreatedBy;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt; =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;base&lt;/span&gt;.CreatedBy = &lt;span class=&#34;keyword&#34;&gt;value&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The project solution should look like this:&lt;/p&gt;
&lt;img src=&#34;/2020/11/25/Auditing-with-Mediator-Pipelines-in-ASP-NET-Core/01_project_structure_auditing.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Open your &lt;code&gt;ProductsController.cs&lt;/code&gt; file and set the CreatedBy property of all the commands and queries with the property &lt;code&gt;User.Identity.Name&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Please keep in your mind that, since we haven’t configured authentication in this API, the username value will always be null.&lt;/p&gt;
&lt;p&gt;After changes, your controller actions should look as follows:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;Route(&lt;span class=&#34;string&#34;&gt;&amp;quot;products&amp;quot;&lt;/span&gt;)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProductsController&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;ControllerBase&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; IMediator _mediator;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ProductsController&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IMediator mediator&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _mediator = mediator;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;HttpGet&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task&amp;lt;IEnumerable&amp;lt;ProductModel&amp;gt;&amp;gt; SearchAsync([FromQuery] &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; filterQ, [FromQuery] &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;? skip, [FromQuery] &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;? take, CancellationToken ct)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; result = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _mediator.FetchAsync(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; SearchProductsQuery&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            FilterQ = filterQ,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Skip = skip,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Take = take,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedBy = User.Identity.Name&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; result.Select(r =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ProductModel&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Id = r.Id,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Code = r.Code,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Name = r.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Price = r.Price&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;HttpGet(&lt;span class=&#34;string&#34;&gt;&amp;quot;&amp;#123;id:guid&amp;#125;&amp;quot;&lt;/span&gt;)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task&amp;lt;ProductModel&amp;gt; &lt;span class=&#34;title&#34;&gt;GetByIdAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;[FromRoute] Guid id, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; result = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _mediator.FetchAsync(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; GetProductByIdQuery&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ProductId = id,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedBy = User.Identity.Name&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ProductModel&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Id = result.Id,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Code = result.Code,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Name = result.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Price = result.Price&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;HttpPost&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task&amp;lt;CreateProductResultModel&amp;gt; &lt;span class=&#34;title&#34;&gt;CreateAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;[FromBody] CreateProductModel model, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; result = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _mediator.SendAsync(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CreateProductCommand&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Code = model.Code,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Name = model.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Price = model.Price,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedBy = User.Identity.Name&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CreateProductResultModel&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Id = result.Id&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;HttpPut(&lt;span class=&#34;string&#34;&gt;&amp;quot;&amp;#123;id:guid&amp;#125;&amp;quot;&lt;/span&gt;)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;UpdateAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;[FromRoute] Guid id, [FromBody] UpdateProductModel model, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _mediator.SendAsync(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; UpdateProductCommand&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ProductId = id,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Code = model.Code,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Name = model.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Price = model.Price,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedBy = User.Identity.Name&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;HttpDelete(&lt;span class=&#34;string&#34;&gt;&amp;quot;&amp;#123;id:guid&amp;#125;&amp;quot;&lt;/span&gt;)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;DeleteAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;[FromRoute] Guid id, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _mediator.SendAsync(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; DeleteProductCommand&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ProductId = id,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedBy = User.Identity.Name&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;We also want to pass the same username to all of our events, so open the command handlers and set the event &lt;code&gt;CreatedBy&lt;/code&gt; property with the same value from the command, as exemplified by the following handler:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CreateProductCommandHandler&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;ICommandHandler&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;CreateProductCommand&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;CreateProductResult&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; ApiDbContext _context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; IMediator _mediator;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CreateProductCommandHandler&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;ApiDbContext context, IMediator mediator&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _context = context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _mediator = mediator;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task&amp;lt;CreateProductResult&amp;gt; &lt;span class=&#34;title&#34;&gt;HandleAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CreateProductCommand cmd, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; products = _context.Set&amp;lt;ProductEntity&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; products.AnyAsync(p =&amp;gt; p.Code == cmd.Code, ct))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; InvalidOperationException(&lt;span class=&#34;string&#34;&gt;$&amp;quot;Product code &amp;#x27;&lt;span class=&#34;subst&#34;&gt;&amp;#123;cmd.Code&amp;#125;&lt;/span&gt;&amp;#x27; already exists&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; externalId = Guid.NewGuid();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; products.AddAsync(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ProductEntity&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ExternalId = externalId,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Code = cmd.Code,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Name = cmd.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Price = cmd.Price&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _mediator.BroadcastAsync(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CreatedProductEvent&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ExternalId = externalId,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Code = cmd.Code,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Name = cmd.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Price = cmd.Price,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedBy = cmd.CreatedBy  &lt;span class=&#34;comment&#34;&gt;// use the same value&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CreateProductResult&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Id = externalId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;The-Audit-Pipeline&#34;&gt;&lt;a href=&#34;#The-Audit-Pipeline&#34; class=&#34;headerlink&#34; title=&#34;The Audit Pipeline&#34;&gt;&lt;/a&gt;The Audit Pipeline&lt;/h1&gt;&lt;p&gt;Now that we are passing the user information into the mediator we can create the audit pipeline that will have the following behavior when intercepting commands:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Serialize and insert a new entry into the commands table;&lt;/li&gt;
&lt;li&gt;Add both the command and entry ids into an &lt;code&gt;AsyncLocal&amp;lt;T&amp;gt;&lt;/code&gt; scope to be used if an event is broadcast;&lt;/li&gt;
&lt;li&gt;Invoke the next pipe;&lt;/li&gt;
&lt;li&gt;If available, serialize the result, calculate the execution time and update the table entry;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When intercepting events, which are sent by commands, it will do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get the command id from the current &lt;code&gt;AsyncLocal&amp;lt;T&amp;gt;&lt;/code&gt; scope;&lt;/li&gt;
&lt;li&gt;Serialize the event and insert a new entry into the events table, referencing the command entry;&lt;/li&gt;
&lt;li&gt;Invoke the next pipe;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Inside the &lt;code&gt;Pipelines&lt;/code&gt; folder, create an &lt;code&gt;AuditPipeline&lt;/code&gt; class extending &lt;code&gt;Pipeline&lt;/code&gt;. The implementation should be similar to the following:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;97&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;98&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;99&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;AuditPipeline&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;Pipeline&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; DbSet&amp;lt;CommandEntity&amp;gt; _commands;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; DbSet&amp;lt;EventEntity&amp;gt; _events;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;AuditPipeline&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;ApiDbContext context&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _commands = context.Set&amp;lt;CommandEntity&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _events = context.Set&amp;lt;EventEntity&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;OnCommandAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TCommand&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TCommand, CancellationToken, Task&amp;gt; next, TCommand cmd, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; command = (&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _commands.AddAsync(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CommandEntity&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ExternalId = cmd.Id,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Name = &lt;span class=&#34;keyword&#34;&gt;typeof&lt;/span&gt;(TCommand).Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Payload = JsonSerializer.Serialize(cmd),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Result = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedOn = cmd.CreatedOn,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedBy = cmd.CreatedBy,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ExecutionTime = TimeSpan.Zero&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct)).Entity;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; (CommandScope.Begin(command.ExternalId, command.Id))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(cmd, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        command.ExecutionTime = DateTimeOffset.UtcNow - cmd.CreatedOn;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;OnCommandAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TCommand&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TCommand, CancellationToken, Task&amp;lt;TResult&amp;gt;&amp;gt; next, TCommand cmd, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; command = (&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _commands.AddAsync(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CommandEntity&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ExternalId = cmd.Id,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Name = &lt;span class=&#34;keyword&#34;&gt;typeof&lt;/span&gt;(TCommand).Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Payload = JsonSerializer.Serialize(cmd),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Result = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedOn = cmd.CreatedOn,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedBy = cmd.CreatedBy,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ExecutionTime = TimeSpan.Zero&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct)).Entity;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        TResult result;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; (CommandScope.Begin(command.ExternalId, command.Id))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            result = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(cmd, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        command.Result = result == &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt; ? &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt; : JsonSerializer.Serialize(result);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        command.ExecutionTime = DateTimeOffset.UtcNow - cmd.CreatedOn;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; result;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;OnEventAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TEvent&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TEvent, CancellationToken, Task&amp;gt; next, TEvent evt, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _events.AddAsync(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; EventEntity&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CommandId = CommandScope.Current.Id,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ExternalId = evt.Id,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Name = &lt;span class=&#34;keyword&#34;&gt;typeof&lt;/span&gt;(TEvent).Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Payload = JsonSerializer.Serialize(evt),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedOn = evt.CreatedOn,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedBy = evt.CreatedBy,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(evt, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CommandScope&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IDisposable&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CommandScope&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;Guid externalId, &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; id&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ExternalId = externalId;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Id = id;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            AsyncLocal.Value = &lt;span class=&#34;keyword&#34;&gt;this&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Guid ExternalId &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; Id &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Dispose&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            AsyncLocal.Value = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; AsyncLocal&amp;lt;CommandScope&amp;gt; AsyncLocal = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; AsyncLocal&amp;lt;CommandScope&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; CommandScope Current =&amp;gt; AsyncLocal.Value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; IDisposable &lt;span class=&#34;title&#34;&gt;Begin&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;Guid externalId, &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; id&lt;/span&gt;)&lt;/span&gt; =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; CommandScope(externalId, id);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Open the &lt;code&gt;Startup.cs&lt;/code&gt; file and register this pipeline to be run after all the existing ones, right before the commands or events are handled.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Startup&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ConfigureServices&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IServiceCollection services&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddMvc();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddSwaggerGen();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddDbContext&amp;lt;ApiDbContext&amp;gt;(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.UseInMemoryDatabase(&lt;span class=&#34;string&#34;&gt;&amp;quot;ApiDbContext&amp;quot;&lt;/span&gt;).ConfigureWarnings(warn =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;comment&#34;&gt;// since InMemoryDatabase does not support transactions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;comment&#34;&gt;// for test purposes we are going to ignore this exception&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                warn.Ignore(InMemoryEventId.TransactionIgnoredWarning);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddMediator(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;LoggingPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;TimeoutPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;ValidationPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;TransactionPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;AuditPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; implementationType &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;typeof&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;Startup&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;                .Assembly&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;                .ExportedTypes&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;                .&lt;span class=&#34;title&#34;&gt;Where&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;t =&amp;gt; t.IsClass &amp;amp;&amp;amp; !t.IsAbstract&lt;/span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; serviceType &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; implementationType&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    .GetInterfaces()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    .Where(i =&amp;gt; i.IsGenericType &amp;amp;&amp;amp; i.GetGenericTypeDefinition() == &lt;span class=&#34;keyword&#34;&gt;typeof&lt;/span&gt;(IValidator&amp;lt;&amp;gt;)))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    o.Services.Add(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddHandlersFromAssemblyOf&amp;lt;Startup&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// or, if using the SimpleSoft.Mediator.Microsoft.Extensions.* pipelines&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//services.AddMediator(o =&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    o.AddPipelineForLogging(options =&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//        options.LogCommandResult = true;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//        options.LogQueryResult = true;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    &amp;#125;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    o.AddPipeline&amp;lt;TimeoutPipeline&amp;gt;();&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    o.AddPipelineForValidation(options =&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//        options.ValidateCommand = true;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//        options.ValidateEvent = true;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    &amp;#125;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    o.AddPipelineForEFCoreTransaction&amp;lt;ApiDbContext&amp;gt;(options =&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//        options.BeginTransactionOnCommand = true;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    &amp;#125;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    o.AddPipeline&amp;lt;AuditPipeline&amp;gt;();&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    o.AddValidatorsFromAssemblyOf&amp;lt;Startup&amp;gt;();&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    o.AddHandlersFromAssemblyOf&amp;lt;Startup&amp;gt;();&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//&amp;#125;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Because this pipeline is also serializing all events, the existing handlers for &lt;code&gt;CreatedProductEvent&lt;/code&gt;, &lt;code&gt;DeletedProductEvent&lt;/code&gt; and &lt;code&gt;UpdatedProductEvent&lt;/code&gt; can now either be deleted or stop storing their events into the table to prevent duplicated data:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CreatedProductEventHandler&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IEventHandler&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;CreatedProductEvent&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; ApiDbContext _context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CreatedProductEventHandler&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;ApiDbContext context&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _context = context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;HandleAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CreatedProductEvent evt, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//await _context.Set&amp;lt;EventEntity&amp;gt;().AddAsync(new EventEntity&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    ExternalId = evt.Id,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    Name = nameof(CreatedProductEvent),&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    Payload = JsonSerializer.Serialize(evt),&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    CreatedOn = evt.CreatedOn,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//    CreatedBy = evt.CreatedBy&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//&amp;#125;, ct);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Task.CompletedTask;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;AsyncLocal&#34;&gt;&lt;a href=&#34;#AsyncLocal&#34; class=&#34;headerlink&#34; title=&#34;AsyncLocal&#34;&gt;&lt;/a&gt;AsyncLocal&lt;T&gt;&lt;/h1&gt;&lt;p&gt;When comparing the audit pipeline with the previous ones we implemented, the biggest difference is the usage of &lt;code&gt;AsyncLocal&amp;lt;T&amp;gt;&lt;/code&gt; to store an instance of the &lt;code&gt;CommandScope&lt;/code&gt; class holding both the command external id and the primary key value for the audit entry into the table.&lt;/p&gt;
&lt;p&gt;If you aren’t familiar with this class, it is available since .NET Framework 4.6 and .NET standard 1.3, and was introduced to help sharing global flow state when implementing asynchronous code with Task Parallel Library (TPL). Because TPL relies on the thread pool and, by default, asynchronous code in ASP.NET Core applications can be resumed by any available thread, we can’t rely on mechanisms like the &lt;code&gt;ThreadLocal&lt;/code&gt; class to store global state.&lt;/p&gt;
&lt;p&gt;Simply put, the idea of &lt;code&gt;AsyncLocal&amp;lt;T&amp;gt;&lt;/code&gt; is to create a static instance that can hold some &lt;code&gt;T&lt;/code&gt; value and, as long you use the &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; keywords, the runtime will consider your code execution to be a logical flow, despite asynchronous, and will ensure the value is shared even if the flow has been resumed by a different thread.&lt;/p&gt;
&lt;p&gt;Because we want to share data between the command and event interceptor code, the flow is asynchronous, and since only commands broadcast events, the &lt;code&gt;AsyncLocal&amp;lt;T&amp;gt;&lt;/code&gt; class is an elegant solution to prevent changing all the events to include an &lt;code&gt;CommandId&lt;/code&gt; property that has to be set on every broadcast.&lt;/p&gt;
&lt;p&gt;As an example, this is usually the solution implemented by some logging frameworks to support the creation of scopes, enabling some information to be written on every log without having to pass it every time, like when the using Microsoft façade &lt;code&gt;Logger.BeginScope(&amp;quot;X:&amp;#123;x&amp;#125; Y:&amp;#123;y&amp;#125;&amp;quot;, x, y)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For more details and examples, give a look to the &lt;code&gt;AsyncLocal&amp;lt;T&amp;gt;&lt;/code&gt; class &lt;a href=&#34;https://docs.microsoft.com/en-us/dotnet/api/system.threading.asynclocal-1?view=net-8.0&#34;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;Audits-Controller&#34;&gt;&lt;a href=&#34;#Audits-Controller&#34; class=&#34;headerlink&#34; title=&#34;Audits Controller&#34;&gt;&lt;/a&gt;Audits Controller&lt;/h1&gt;&lt;p&gt;To make it easier to test and check our system audits, we are going to implement the following endpoint:&lt;/p&gt;
&lt;p&gt;GET &amp;#x2F;audits — search for command audits using some filters (&lt;code&gt;SearchAuditsQuery&lt;/code&gt;);&lt;br&gt;GET &amp;#x2F;audits&amp;#x2F;{id} — get a command audit by its unique identifier and all the associated events (&lt;code&gt;GetAuditByIdQuery&lt;/code&gt;);&lt;/p&gt;
&lt;p&gt;Inside the &lt;code&gt;Handlers&lt;/code&gt; folder create an &lt;code&gt;Audits&lt;/code&gt; folder and create the queries for searching or getting an audit by its external id:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;97&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;98&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;99&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;100&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;101&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;102&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;103&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;104&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;105&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;106&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;107&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;108&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;109&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;110&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;111&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;112&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;113&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;114&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;115&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;116&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;117&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;118&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;119&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;120&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;121&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;122&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;123&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;124&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;125&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SearchAuditsQueryHandler&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IQueryHandler&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;SearchAuditsQuery&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;AuditSearchItem&lt;/span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; IQueryable&amp;lt;CommandEntity&amp;gt; _commands;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SearchAuditsQueryHandler&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;ApiDbContext context&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _commands = context.Set&amp;lt;CommandEntity&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task&amp;lt;IEnumerable&amp;lt;AuditSearchItem&amp;gt;&amp;gt; HandleAsync(SearchAuditsQuery query, CancellationToken ct)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; filter = _commands;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;.IsNullOrWhiteSpace(query.FilterQ))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; filterQ = query.FilterQ.Trim();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            filter = filter.Where(p =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                p.Name.Contains(filterQ)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; skip = query.Skip ?? &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; take = query.Take ?? &lt;span class=&#34;number&#34;&gt;20&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; filter&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .OrderByDescending(c =&amp;gt; c.CreatedOn)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .ThenByDescending(c =&amp;gt; c.Id)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .Skip(skip)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .Take(take)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .Select(c =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; AuditSearchItem&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Id = c.ExternalId,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Name = c.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                CreatedOn = c.CreatedOn,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                CreatedBy = c.CreatedBy,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                ExecutionTimeInMs = (&lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt;) c.ExecutionTime.TotalMilliseconds&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;).ToListAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;SearchAuditsQuery&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;Query&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;AuditSearchItem&lt;/span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; FilterQ &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;? Skip &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;? Take &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;AuditSearchItem&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Guid Id &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Name &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; DateTimeOffset CreatedOn &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; CreatedBy &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; ExecutionTimeInMs &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;GetAuditByIdQueryHandler&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IQueryHandler&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;GetAuditByIdQuery&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;Audit&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; IQueryable&amp;lt;CommandEntity&amp;gt; _commands;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; IQueryable&amp;lt;EventEntity&amp;gt; _events;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;GetAuditByIdQueryHandler&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;ApiDbContext context&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _commands = context.Set&amp;lt;CommandEntity&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _events = context.Set&amp;lt;EventEntity&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task&amp;lt;Audit&amp;gt; &lt;span class=&#34;title&#34;&gt;HandleAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;GetAuditByIdQuery query, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; command = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _commands.SingleOrDefaultAsync(c =&amp;gt; c.ExternalId == query.AuditId, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (command == &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; InvalidOperationException(&lt;span class=&#34;string&#34;&gt;$&amp;quot;Command audit &amp;#x27;&lt;span class=&#34;subst&#34;&gt;&amp;#123;query.AuditId&amp;#125;&lt;/span&gt;&amp;#x27; not found&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; events = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _events.Where(e =&amp;gt; e.CommandId == command.Id).ToListAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; Audit&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Id = command.ExternalId,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Name = command.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Payload = JsonSerializer.Deserialize&amp;lt;&lt;span class=&#34;built_in&#34;&gt;dynamic&lt;/span&gt;&amp;gt;(command.Payload),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Result = &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt;.IsNullOrWhiteSpace(command.Result)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                ? &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                : JsonSerializer.Deserialize&amp;lt;&lt;span class=&#34;built_in&#34;&gt;dynamic&lt;/span&gt;&amp;gt;(command.Result),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedOn = command.CreatedOn,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedBy = command.CreatedBy,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ExecutionTimeInMs = (&lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt;) command.ExecutionTime.TotalMilliseconds,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Events = events.Select(e =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; AuditEvent&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Id = e.ExternalId,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Name = e.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Payload = JsonSerializer.Deserialize&amp;lt;&lt;span class=&#34;built_in&#34;&gt;dynamic&lt;/span&gt;&amp;gt;(e.Payload),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                CreatedOn = e.CreatedOn&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;).ToList()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;GetAuditByIdQuery&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;Query&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;Audit&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Guid AuditId &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Audit&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Guid Id &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Name &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; Payload &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; Result &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; DateTimeOffset CreatedOn &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; CreatedBy &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; ExecutionTimeInMs &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; IEnumerable&amp;lt;AuditEvent&amp;gt; Events &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;AuditEvent&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Guid Id &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Name &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; Payload &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; DateTimeOffset CreatedOn &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Inside the &lt;code&gt;Controllers&lt;/code&gt; folder create an &lt;code&gt;Audit&lt;/code&gt; folder and an &lt;code&gt;AuditController&lt;/code&gt; that will use the previous queries:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;[&lt;span class=&#34;meta&#34;&gt;Route(&lt;span class=&#34;string&#34;&gt;&amp;quot;audits&amp;quot;&lt;/span&gt;)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;AuditsController&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;ControllerBase&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; IMediator _mediator;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;AuditsController&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IMediator mediator&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _mediator = mediator;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;HttpGet&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task&amp;lt;IEnumerable&amp;lt;AuditSearchItemModel&amp;gt;&amp;gt; SearchAsync([FromQuery] &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; filterQ, [FromQuery] &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;? skip, [FromQuery] &lt;span class=&#34;built_in&#34;&gt;int&lt;/span&gt;? take, CancellationToken ct)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; result = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _mediator.FetchAsync(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; SearchAuditsQuery&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            FilterQ = filterQ,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Skip = skip,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Take = take,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedBy = User.Identity.Name&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; result.Select(r =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; AuditSearchItemModel&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Id = r.Id,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Name = r.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedOn = r.CreatedOn,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedBy = r.CreatedBy,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ExecutionTimeInMs = r.ExecutionTimeInMs&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    [&lt;span class=&#34;meta&#34;&gt;HttpGet(&lt;span class=&#34;string&#34;&gt;&amp;quot;&amp;#123;id:guid&amp;#125;&amp;quot;&lt;/span&gt;)&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task&amp;lt;AuditModel&amp;gt; &lt;span class=&#34;title&#34;&gt;GetByIdAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;[FromRoute] Guid id, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; result = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _mediator.FetchAsync(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; GetAuditByIdQuery&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            AuditId = id,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedBy = User.Identity.Name&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; AuditModel&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Id = result.Id,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Name = result.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Payload = result.Payload,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Result = result.Result,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedOn = result.CreatedOn,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            CreatedBy = result.CreatedBy,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ExecutionTimeInMs = result.ExecutionTimeInMs,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Events = result.Events.Select(e =&amp;gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; AuditEventModel&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Id = e.Id,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Name = e.Name,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                Payload = e.Payload,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                CreatedOn = e.CreatedOn&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;AuditSearchItemModel&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Guid Id &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Name &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; DateTimeOffset CreatedOn &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; CreatedBy &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; ExecutionTimeInMs &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;AuditModel&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Guid Id &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Name &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; Payload &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; Result &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; DateTimeOffset CreatedOn &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; CreatedBy &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;long&lt;/span&gt; ExecutionTimeInMs &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; IEnumerable&amp;lt;AuditEventModel&amp;gt; Events &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;AuditEventModel&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Guid Id &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; Name &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;object&lt;/span&gt; Payload &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; DateTimeOffset CreatedOn &amp;#123; &lt;span class=&#34;keyword&#34;&gt;get&lt;/span&gt;; &lt;span class=&#34;keyword&#34;&gt;set&lt;/span&gt;; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The project structure should look as follows:&lt;/p&gt;
&lt;img src=&#34;/2020/11/25/Auditing-with-Mediator-Pipelines-in-ASP-NET-Core/02_project_structure_auditing_final.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Open the Swagger endpoint (ex: &lt;a href=&#34;https://localhost:44380/swagger&#34;&gt;https://localhost:44380/swagger&lt;/a&gt;) and you should see the audits endpoint:&lt;/p&gt;
&lt;img src=&#34;/2020/11/25/Auditing-with-Mediator-Pipelines-in-ASP-NET-Core/03_swagger_audit.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Create, update or delete products with the help of Swagger UI and then check if all the commands and events have been properly audited:&lt;/p&gt;
&lt;img src=&#34;/2020/11/25/Auditing-with-Mediator-Pipelines-in-ASP-NET-Core/04_swagger_audit_search.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;And even get the details of a specific audit:&lt;/p&gt;
&lt;img src=&#34;/2020/11/25/Auditing-with-Mediator-Pipelines-in-ASP-NET-Core/05_swagger_audit_get_by_id.png&#34; class=&#34;&#34;&gt;

&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;I hope this article gave you a good idea on how to use mediator pipelines to simplify the auditing of user actions without having to replicate code across all commands.&lt;/p&gt;
&lt;p&gt;We also ensured events were always stored before being broadcasted and a reference to the command was kept without adding properties to our POCOs, providing a more clean approach.&lt;/p&gt;
&lt;p&gt;Soon I’ll be explaining how we can inject more specialized interfaces, like the &lt;code&gt;ISender&amp;lt;TCommand&amp;gt;&lt;/code&gt;, to make our dependencies more clearer and help with unit testing.&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="aspnetcore" />
        <category term="csharp" />
        <category term="patterns" />
        <updated>2020-11-25T00:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2020/11/07/Transaction-Management-With-Mediator-Pipelines-in-ASP-NET-Core/</id>
        <title>Transaction Management With Mediator Pipelines in ASP.NET Core</title>
        <link rel="alternate" href="https://code-corner.dev/2020/11/07/Transaction-Management-With-Mediator-Pipelines-in-ASP-NET-Core/"/>
        <content type="html">&lt;p&gt;When implementing a &lt;strong&gt;simplified CQRS&lt;/strong&gt; service, which usually has a single database but still separates commands from queries, the mediator pipeline can be used to manage transactions when processing commands, ensuring changes will be implicitly committed unless an exception is thrown.&lt;/p&gt;
&lt;p&gt;Implementing a pipeline for transactions has some key advantages even when using Entity Framework Core or other ORM that tracks and flushes changes at a single point in time.&lt;/p&gt;
&lt;p&gt;Because the command handler is executing inside an implicit transaction, the developer is free to flush their changes as it sees fit knowing that any exception will rollback the changes. This is very helpful when it wants to cancel the flow when some business rules aren’t meet and could only be checked after changing the system state. Optimistic concurrency is a good example where you may want to flush your changes before leaving the handler so you know exactly what entity failed and can send a more detailed message to the user, which is the usual approach when implementing a dedicated &lt;strong&gt;Backend for Frontend&lt;/strong&gt; service.&lt;/p&gt;
&lt;p&gt;Also, ORMs are very good at abstracting database access but can’t solve more edge cases, specially when performance is a requirement. Being able to use a more lightweight library (e.g. &lt;a href=&#34;https://github.com/StackExchange/Dapper&#34;&gt;Dapper&lt;/a&gt;) to do some bulk operations and using the ORM context connection inside the same transaction will remove the need to use the &lt;code&gt;TransactionScope&lt;/code&gt; and reduce some application complexity.&lt;/p&gt;
&lt;p&gt;Ensuring transactions are properly managed around the handler execution ensure the application architecture is be more robust and will be able to handle more edge cases.&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id=&#34;The-project&#34;&gt;&lt;a href=&#34;#The-project&#34; class=&#34;headerlink&#34; title=&#34;The project&#34;&gt;&lt;/a&gt;The project&lt;/h1&gt;&lt;p&gt;This article will be based on my previous ones that explained how to use the mediator and implement transversal behavior with pipelines. As a reminder, we implemented an endpoint to manage products with the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GET &amp;#x2F;products — search for products using some filters (&lt;code&gt;SearchProductsQuery&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;GET &amp;#x2F;products&amp;#x2F;{id} — get a product by its unique identifier (&lt;code&gt;GetProductByIdQuery&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;POST &amp;#x2F;products — create a product (&lt;code&gt;CreateProductCommand&lt;/code&gt; and &lt;code&gt;CreatedProductEvent&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;PUT &amp;#x2F;products&amp;#x2F;{id} — update a product by its unique identifier (&lt;code&gt;UpdateProductCommand&lt;/code&gt; and &lt;code&gt;UpdatedProductEvent&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;DELETE &amp;#x2F;products&amp;#x2F;{id} — delete a product by its unique identifier (&lt;code&gt;DeleteProductCommand&lt;/code&gt; and &lt;code&gt;DeletedProductEvent&lt;/code&gt;);&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can check them out here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/2020/10/28/Mediator-Pattern-in-ASP-NET-Core-Applications/&#34; title=&#34;Mediator Pattern in ASP.NET Core Applications&#34;&gt;Mediator Pattern in ASP.NET Core Applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/2020/11/02/Using-Mediator-Pipelines-in-ASP-NET-Core-Applications/&#34; title=&#34;Using Mediator Pipelines in ASP.NET Core Applications&#34;&gt;Using Mediator Pipelines in ASP.NET Core Applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/2020/11/04/Validation-with-Mediator-Pipelines-in-ASP-NET-Core-Applications/&#34; title=&#34;Validation with Mediator Pipelines in ASP.NET Core Applications&#34;&gt;Validation with Mediator Pipelines in ASP.NET Core Applications&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The source code is available on &lt;a href=&#34;https://github.com/gravity00/IntroductionMediatorCQRS&#34;&gt;GitHub&lt;/a&gt;, feel free to give it a look.&lt;/p&gt;
&lt;h1 id=&#34;Entity-Framework-Core-transactions&#34;&gt;&lt;a href=&#34;#Entity-Framework-Core-transactions&#34; class=&#34;headerlink&#34; title=&#34;Entity Framework Core transactions&#34;&gt;&lt;/a&gt;Entity Framework Core transactions&lt;/h1&gt;&lt;p&gt;Entity Framework Core has support for explicit transaction management, which works in a similar way to ADO.NET or the &lt;code&gt;TransactionScope&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;You can get a disposable instance of &lt;code&gt;IDbContextTransaction&lt;/code&gt; by invoking the method &lt;code&gt;BeginTransactionAsync&lt;/code&gt;, available from the database property. The transaction can be either committed or rolled back, either by explicit invocation or by disposing the instance before any commit.&lt;/p&gt;
&lt;p&gt;As stated before, this can be very helpful if &lt;code&gt;SaveChanges&lt;/code&gt; must be invoked multiple times inside the &lt;em&gt;unit of work&lt;/em&gt; operation and you want to ensure exceptions will rollback data changes.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; tx = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.BeginTransactionAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; products = _context.Set&amp;lt;ProductEntity&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; products.Where(p =&amp;gt; p.Code.StartsWith(&lt;span class=&#34;string&#34;&gt;&amp;#x27;9&amp;#x27;&lt;/span&gt;)).ForEachAsync(product =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    product.Name += &lt;span class=&#34;string&#34;&gt;&amp;quot; [DEPRECATED]&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.SaveChangesAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; products.ForEachAsync(product =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    product.Price /= &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.SaveChangesAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; tx.CommitAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;In the example above if an exception was thrown after the first &lt;code&gt;SaveChangesAsync&lt;/code&gt; every changes would be reverted because the transaction would be disposed without an explicit call to commit.&lt;/p&gt;
&lt;p&gt;We are going to leverage on this behavior to implement the pipeline.&lt;/p&gt;
&lt;h1 id=&#34;The-pipeline&#34;&gt;&lt;a href=&#34;#The-pipeline&#34; class=&#34;headerlink&#34; title=&#34;The pipeline&#34;&gt;&lt;/a&gt;The pipeline&lt;/h1&gt;&lt;p&gt;Since only commands mutate system state, we are going to use the command pipeline to enforce a transaction for the next pipelines in the chain up until the handler.&lt;/p&gt;
&lt;p&gt;The pipeline behavior will be:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Intercept any command;&lt;/li&gt;
&lt;li&gt;Create a new &lt;code&gt;IDbContextTransaction&lt;/code&gt; with &lt;code&gt;BeginTransactionAsync&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Invoke the next pipeline inside the &lt;code&gt;using&lt;/code&gt; scope;&lt;/li&gt;
&lt;li&gt;If no exception is thrown, flush all changes and commit;&lt;/li&gt;
&lt;li&gt;Dispose the transaction;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’ll also include some commented code incase you want to be sure the queries never mutate the system state. Prevention is better than cure…&lt;/p&gt;
&lt;p&gt;Inside the &lt;code&gt;Pipelines&lt;/code&gt; folder create a &lt;code&gt;TransactionPipeline&lt;/code&gt; class extending &lt;code&gt;Pipeline&lt;/code&gt; (because we only need some of the methods):&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;TransactionPipeline&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;Pipeline&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; ApiDbContext _context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;TransactionPipeline&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;ApiDbContext context&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _context = context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;OnCommandAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TCommand&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TCommand, CancellationToken, Task&amp;gt; next, TCommand cmd, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; tx = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.BeginTransactionAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(cmd, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.SaveChangesAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; tx.CommitAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;OnCommandAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TCommand&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TCommand, CancellationToken, Task&amp;lt;TResult&amp;gt;&amp;gt; next, TCommand cmd, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; tx = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.Database.BeginTransactionAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; result = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(cmd, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _context.SaveChangesAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; tx.CommitAsync(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; result;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;//public override async Task&amp;lt;TResult&amp;gt; OnQueryAsync&amp;lt;TQuery, TResult&amp;gt;(Func&amp;lt;TQuery, CancellationToken, Task&amp;lt;TResult&amp;gt;&amp;gt; next, TQuery query, CancellationToken ct)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;//&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;//    await using var tx = await _context.Database.BeginTransactionAsync(ct);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;//    var result = await next(query, ct);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;//    await tx.RollbackAsync(ct);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;//    return result;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;//&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Open the &lt;code&gt;Startup.cs&lt;/code&gt; file and add the &lt;code&gt;TransactionPipeline&lt;/code&gt; after the &lt;code&gt;ValidationPipeline&lt;/code&gt;, preventing the opening of transactions that could be immediately closed if the command had invalid data.&lt;/p&gt;
&lt;p&gt;Since this examples are using the in-memory database provider for Entity Framework Core, which does not support explicit transactions, we are also going to ignore them for test purposes.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Startup&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ConfigureServices&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IServiceCollection services&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddMvc();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddSwaggerGen();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddDbContext&amp;lt;ApiDbContext&amp;gt;(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.UseInMemoryDatabase(&lt;span class=&#34;string&#34;&gt;&amp;quot;ApiDbContext&amp;quot;&lt;/span&gt;).ConfigureWarnings(warn =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                warn.Ignore(InMemoryEventId.TransactionIgnoredWarning);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddMediator(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;LoggingPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;TimeoutPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;ValidationPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;TransactionPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; implementationType &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;typeof&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;Startup&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;                .Assembly&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;                .ExportedTypes&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;                .&lt;span class=&#34;title&#34;&gt;Where&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;t =&amp;gt; t.IsClass &amp;amp;&amp;amp; !t.IsAbstract&lt;/span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; serviceType &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; implementationType&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    .GetInterfaces()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    .Where(i =&amp;gt; i.IsGenericType &amp;amp;&amp;amp; i.GetGenericTypeDefinition() == &lt;span class=&#34;keyword&#34;&gt;typeof&lt;/span&gt;(IValidator&amp;lt;&amp;gt;)))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    o.Services.Add(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddHandlersFromAssemblyOf&amp;lt;Startup&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now that we have a transversal behavior that manages transactions and flushes all context changes to the database, we can remove from all command handlers the explicit calls to &lt;code&gt;SaveChangesAsync&lt;/code&gt;:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;DeleteProductCommandHandler&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;ICommandHandler&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;DeleteProductCommand&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; ApiDbContext _context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; IMediator _mediator;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;DeleteProductCommandHandler&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;ApiDbContext context, IMediator mediator&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _context = context;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _mediator = mediator;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;HandleAsync&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;DeleteProductCommand cmd, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; products = _context.Set&amp;lt;ProductEntity&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; product = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; products.SingleOrDefaultAsync(p =&amp;gt; p.ExternalId == cmd.ProductId, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (product == &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; InvalidOperationException(&lt;span class=&#34;string&#34;&gt;$&amp;quot;Product &amp;#x27;&lt;span class=&#34;subst&#34;&gt;&amp;#123;cmd.ProductId&amp;#125;&lt;/span&gt;&amp;#x27; not found&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        products.Remove(product);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;//await _context.SaveChangesAsync(ct);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; _mediator.BroadcastAsync(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; DeletedProductEvent&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ProductId = cmd.ProductId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Speeding-things-up&#34;&gt;&lt;a href=&#34;#Speeding-things-up&#34; class=&#34;headerlink&#34; title=&#34;Speeding things up!&#34;&gt;&lt;/a&gt;Speeding things up!&lt;/h1&gt;&lt;p&gt;Because this is a pipeline we use very often in most of our APIs, there is already a &lt;a href=&#34;https://www.nuget.org/packages/simplesoft.mediator.microsoft.extensions.efcoretransactionpipeline&#34;&gt;NuGet available that allows to open an explicit Entity Framework Core transaction for commands, events or queries&lt;/a&gt;, depending your needs.&lt;/p&gt;
&lt;p&gt;To use it, just open the NuGet package manager and install the package &lt;code&gt;SimpleSoft.Mediator.Microsoft.Extensions.EFCoreTransactionPipeline&lt;/code&gt;:&lt;/p&gt;
&lt;img src=&#34;/2020/11/07/Transaction-Management-With-Mediator-Pipelines-in-ASP-NET-Core/01_nuget_mediator_transaction_pipeline.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Open the &lt;code&gt;Startup.cs&lt;/code&gt; file and register the pipeline with the extension method &lt;code&gt;AddPipelineForEFCoreTransaction&amp;lt;TContext&amp;gt;&lt;/code&gt;. Explicit transactions are disabled by default, so you only need to enable them for commands.&lt;/p&gt;
&lt;p&gt;When using all the NuGets mentioned on my previous articles, the &lt;code&gt;Startup.cs&lt;/code&gt; file should be similar to this:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Startup&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ConfigureServices&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IServiceCollection services&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddMvc();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddSwaggerGen();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddDbContext&amp;lt;ApiDbContext&amp;gt;(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.UseInMemoryDatabase(&lt;span class=&#34;string&#34;&gt;&amp;quot;ApiDbContext&amp;quot;&lt;/span&gt;).ConfigureWarnings(warn =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;comment&#34;&gt;// since InMemoryDatabase does not support transactions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;comment&#34;&gt;// for test purposes we are going to ignore this exception&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                warn.Ignore(InMemoryEventId.TransactionIgnoredWarning);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddMediator(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipelineForLogging(options =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                options.LogCommandResult = &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                options.LogQueryResult = &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;TimeoutPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipelineForValidation(options =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                options.ValidateCommand = &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                options.ValidateEvent = &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipelineForEFCoreTransaction&amp;lt;ApiDbContext&amp;gt;(options =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                options.BeginTransactionOnCommand = &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddValidatorsFromAssemblyOf&amp;lt;Startup&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddHandlersFromAssemblyOf&amp;lt;Startup&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ..&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;I hope this article gave you a good idea on how to use mediator pipelines to simplify the management of Entity Framework Core transactions, making the implementation of command handlers less error prone since developers won’t forget to flush changes to the database (that behavior will be implicit unless an exception is thrown).&lt;/p&gt;
&lt;p&gt;Even if your application is using another ORM or other lightweight libraries (e.g. &lt;a href=&#34;https://github.com/StackExchange/Dapper&#34;&gt;Dapper&lt;/a&gt;), you can easily implement your own pipeline.&lt;/p&gt;
&lt;p&gt;I also made an article about &lt;a href=&#34;/2020/11/25/Auditing-with-Mediator-Pipelines-in-ASP-NET-Core/&#34; title=&#34;Auditing with Mediator Pipelines in ASP.NET Core&#34;&gt;auditing user actions with pipelines&lt;/a&gt;, you may also find it helpful.&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="aspnetcore" />
        <category term="csharp" />
        <category term="patterns" />
        <updated>2020-11-07T00:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2020/11/04/Validation-with-Mediator-Pipelines-in-ASP-NET-Core-Applications/</id>
        <title>Validation with Mediator Pipelines in ASP.NET Core Applications</title>
        <link rel="alternate" href="https://code-corner.dev/2020/11/04/Validation-with-Mediator-Pipelines-in-ASP-NET-Core-Applications/"/>
        <content type="html">&lt;p&gt;When implementing web applications that manipulate information, there is always the need to validate data sent by the clients, ensuring business rules are properly implemented.&lt;/p&gt;
&lt;p&gt;Previously I talked about the implementation of &lt;strong&gt;Command Query Responsibility Segregation (CQRS)&lt;/strong&gt; and &lt;strong&gt;Event Sourcing (ES)&lt;/strong&gt; using a mediator pattern and how to support transversal behavior via pipelines.&lt;/p&gt;
&lt;p&gt;In this article I’m going to demonstrate how validation can be enforced into your commands and events before reaching the handlers, making sure that invalid data will be rejected by the API.&lt;/p&gt;
&lt;p&gt;This approach not only reduces the amount of duplicated code, it also ensures validations won’t be forgotten, unless explicitly stated.&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id=&#34;The-project&#34;&gt;&lt;a href=&#34;#The-project&#34; class=&#34;headerlink&#34; title=&#34;The project&#34;&gt;&lt;/a&gt;The project&lt;/h1&gt;&lt;p&gt;To make it faster to implement the validation pipeline, I’m going to leverage this example on both of my previous articles, in which we implemented an &lt;a href=&#34;/2020/10/28/Mediator-Pattern-in-ASP-NET-Core-Applications/&#34; title=&#34;Mediator Pattern in ASP.NET Core Applications&#34;&gt;endpoint to manage products&lt;/a&gt; and introduced both a &lt;a href=&#34;/2020/11/02/Using-Mediator-Pipelines-in-ASP-NET-Core-Applications/&#34; title=&#34;Using Mediator Pipelines in ASP.NET Core Applications&#34;&gt;logging and timeout pipelines&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The source code is available on &lt;a href=&#34;https://github.com/gravity00/IntroductionMediatorCQRS&#34;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;The-validations&#34;&gt;&lt;a href=&#34;#The-validations&#34; class=&#34;headerlink&#34; title=&#34;The validations&#34;&gt;&lt;/a&gt;The validations&lt;/h1&gt;&lt;p&gt;To help us configure the rules we are going to use a very popular and one of my favorites libraries &lt;a href=&#34;https://fluentvalidation.net/&#34;&gt;FluentValidation&lt;/a&gt; by creating classes implementing the interface &lt;code&gt;IValidator&amp;lt;T&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These classes will be added to the dependency injection container and used by the pipeline to validate both the commands and events before reaching the handler.&lt;/p&gt;
&lt;p&gt;Lets start by installing the NuGet &lt;code&gt;FluentValidation&lt;/code&gt;:&lt;/p&gt;
&lt;img src=&#34;/2020/11/04/Validation-with-Mediator-Pipelines-in-ASP-NET-Core-Applications/01_nuget_fluentvalidation.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Create a &lt;code&gt;Validations&lt;/code&gt; folder at the project root level, with a subfolder &lt;code&gt;Products&lt;/code&gt;.&lt;/p&gt;
&lt;img src=&#34;/2020/11/04/Validation-with-Mediator-Pipelines-in-ASP-NET-Core-Applications/02_project_structure.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Inside the &lt;code&gt;Products&lt;/code&gt; folder create both a validator for &lt;code&gt;CreateProductCommand&lt;/code&gt; and &lt;code&gt;CreatedProductEvent&lt;/code&gt; by extending the class &lt;code&gt;AbstractValidator&amp;lt;T&amp;gt;&lt;/code&gt; (which itself implementes the interface &lt;code&gt;IValidator&amp;lt;T&amp;gt;&lt;/code&gt;) and configure some rules in the constructors:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CreateProductCommandValidator&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;AbstractValidator&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;CreateProductCommand&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CreateProductCommandValidator&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        RuleFor(e =&amp;gt; e.Code)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .NotEmpty()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .Length(&lt;span class=&#34;number&#34;&gt;8&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .Matches(&lt;span class=&#34;string&#34;&gt;&amp;quot;^[0-9a-zA-Z]*$&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        RuleFor(e =&amp;gt; e.Name)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .NotEmpty()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .MaximumLength(&lt;span class=&#34;number&#34;&gt;128&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        RuleFor(e =&amp;gt; e.Price)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .GreaterThanOrEqualTo(&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CreatedProductEventValidator&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;AbstractValidator&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;CreatedProductEvent&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;CreatedProductEventValidator&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        RuleFor(e =&amp;gt; e.ExternalId)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .NotEmpty();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        RuleFor(e =&amp;gt; e.Code)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .NotEmpty()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .Length(&lt;span class=&#34;number&#34;&gt;8&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .Matches(&lt;span class=&#34;string&#34;&gt;&amp;quot;^[0-9a-zA-Z]*$&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        RuleFor(e =&amp;gt; e.Name)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .NotEmpty()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .MaximumLength(&lt;span class=&#34;number&#34;&gt;128&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        RuleFor(e =&amp;gt; e.Price)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            .GreaterThanOrEqualTo(&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Feel free to create validators for all other commands and events, I’m just focusing on these for simplicity. You can also check the &lt;a href=&#34;https://docs.fluentvalidation.net/en/latest/&#34;&gt;documentation for supported rules and detailed usage instructions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Open the &lt;code&gt;Startup.cs&lt;/code&gt; file and register all classes implementing &lt;code&gt;IValidator&amp;lt;T&amp;gt;&lt;/code&gt; into the container:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Startup&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ConfigureServices&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IServiceCollection services&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddMvc();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddSwaggerGen();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddDbContext&amp;lt;ApiDbContext&amp;gt;(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.UseInMemoryDatabase(&lt;span class=&#34;string&#34;&gt;&amp;quot;ApiDbContext&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddMediator(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;LoggingPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;TimeoutPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddHandlersFromAssemblyOf&amp;lt;Startup&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; implementationType &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;typeof&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;Startup&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;            .Assembly&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;            .ExportedTypes&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;            .&lt;span class=&#34;title&#34;&gt;Where&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;t =&amp;gt; t.IsClass &amp;amp;&amp;amp; !t.IsAbstract&lt;/span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; serviceType &lt;span class=&#34;keyword&#34;&gt;in&lt;/span&gt; implementationType&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .GetInterfaces()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                .Where(i =&amp;gt; i.IsGenericType &amp;amp;&amp;amp; i.GetGenericTypeDefinition() == &lt;span class=&#34;keyword&#34;&gt;typeof&lt;/span&gt;(IValidator&amp;lt;&amp;gt;)))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                services.Add(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;The-pipeline&#34;&gt;&lt;a href=&#34;#The-pipeline&#34; class=&#34;headerlink&#34; title=&#34;The pipeline&#34;&gt;&lt;/a&gt;The pipeline&lt;/h1&gt;&lt;p&gt;Because for this example we are going to enforce validation only on commands and events, since they either mutate or represent the system state at a given point in time, the pipeline will be implemented as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Intercept any command or event;&lt;/li&gt;
&lt;li&gt;Resolve a required instance of &lt;code&gt;IValidator&amp;lt;TCommand&amp;gt;&lt;/code&gt; or &lt;code&gt;IValidator&amp;lt;TEvent&amp;gt;&lt;/code&gt; from the container;&lt;/li&gt;
&lt;li&gt;Invoke the method &lt;code&gt;ValidateAndThrowAsync&lt;/code&gt;, failing with a &lt;code&gt;ValidationException&lt;/code&gt; if something is invalid;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Inside the &lt;code&gt;Pipelines&lt;/code&gt; folder create a &lt;code&gt;ValidationPipeline&lt;/code&gt; class extending &lt;code&gt;Pipeline&lt;/code&gt; (because we only need some of the methods):&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ValidationPipeline&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;Pipeline&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; IServiceProvider _provider;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ValidationPipeline&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IServiceProvider provider&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _provider = provider;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;OnCommandAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TCommand&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TCommand, CancellationToken, Task&amp;gt; next, TCommand cmd, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; ValidateAndThrowAsync(cmd, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(cmd, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;OnCommandAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TCommand&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TCommand, CancellationToken, Task&amp;lt;TResult&amp;gt;&amp;gt; next, TCommand cmd, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; ValidateAndThrowAsync(cmd, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(cmd, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;OnEventAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TEvent&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TEvent, CancellationToken, Task&amp;gt; next, TEvent evt, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; ValidateAndThrowAsync(evt, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(evt, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;ValidateAndThrowAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;T instance, CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; validator = _provider.GetRequiredService&amp;lt;IValidator&amp;lt;T&amp;gt;&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; validator.ValidateAndThrowAsync(instance, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Open the &lt;code&gt;Startup.cs&lt;/code&gt; file and add the &lt;code&gt;ValidationPipeline&lt;/code&gt; at least after the &lt;code&gt;LoggingPipeline&lt;/code&gt; ensuring, in case invalid data is submitted, we can still see it in the logs:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;services.AddMediator(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    o.AddPipeline&amp;lt;LoggingPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    o.AddPipeline&amp;lt;TimeoutPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    o.AddPipeline&amp;lt;ValidationPipeline();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    o.AddHandlersFromAssemblyOf&amp;lt;Startup&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you now start the server and try to create a product with invalid data you will receive a &lt;code&gt;ValidationException&lt;/code&gt;.&lt;/p&gt;
&lt;img src=&#34;/2020/11/04/Validation-with-Mediator-Pipelines-in-ASP-NET-Core-Applications/03_validation_exception.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Because returning an HTTP 500 due to invalid data would be confusing to the client, lets just finish this example by creating an ASP.NET Core middleware converting this exception into a more appropriate code, like HTTP 422.&lt;/p&gt;
&lt;p&gt;Once again, open the &lt;code&gt;Startup.cs&lt;/code&gt; file and register the middleware immediately after the developer exception page, catching this exception and returning HTTP 422 with a more detailed JSON representation:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Startup&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Configure&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IApplicationBuilder app&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.UseDeveloperExceptionPage();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.Use(&lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; (ctx, next) =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (ValidationException e)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; response = ctx.Response;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (response.HasStarted)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                ctx.RequestServices&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    .GetRequiredService&amp;lt;ILogger&amp;lt;Startup&amp;gt;&amp;gt;()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    .LogWarning(e, &lt;span class=&#34;string&#34;&gt;&amp;quot;Invalid data has been submitted&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                response.Clear();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                response.StatusCode = &lt;span class=&#34;number&#34;&gt;422&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; response.WriteAsync(JsonSerializer.Serialize(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    Message = &lt;span class=&#34;string&#34;&gt;&amp;quot;Invalid data has been submitted&amp;quot;&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    ModelState = e.Errors.ToDictionary(error =&amp;gt; error.ErrorCode, error =&amp;gt; error.ErrorMessage)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;), Encoding.UTF8, ctx.RequestAborted);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.UseSwagger();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.UseSwaggerUI(c =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            c.SwaggerEndpoint(&lt;span class=&#34;string&#34;&gt;&amp;quot;/swagger/v1/swagger.json&amp;quot;&lt;/span&gt;, &lt;span class=&#34;string&#34;&gt;&amp;quot;Mediator ExampleApi V1&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.UseRouting();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.UseEndpoints(endpoints =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            endpoints.MapControllers();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Submit invalid data again and a more detailed message should be returned:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;POST /products&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &amp;quot;code&amp;quot;: &amp;quot;-123456&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &amp;quot;price&amp;quot;: -1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;HTTP 422&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;   &amp;quot;Message&amp;quot;:&amp;quot;Invalid data has been submitted&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;   &amp;quot;ModelState&amp;quot;:&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;quot;ExactLengthValidator&amp;quot;:&amp;quot;&amp;#x27;Code&amp;#x27; must be 8 characters in length. You entered 7 characters.&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;quot;RegularExpressionValidator&amp;quot;:&amp;quot;&amp;#x27;Code&amp;#x27; is not in the correct format.&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;quot;NotEmptyValidator&amp;quot;:&amp;quot;&amp;#x27;Name&amp;#x27; must not be empty.&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;quot;GreaterThanOrEqualValidator&amp;quot;:&amp;quot;&amp;#x27;Price&amp;#x27; must be greater than or equal to &amp;#x27;0&amp;#x27;.&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;   &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Speeding-things-up&#34;&gt;&lt;a href=&#34;#Speeding-things-up&#34; class=&#34;headerlink&#34; title=&#34;Speeding things up!&#34;&gt;&lt;/a&gt;Speeding things up!&lt;/h1&gt;&lt;p&gt;If just like me, you can see yourself using a pipeline for validation in most of your projects, there is already a &lt;a href=&#34;https://www.nuget.org/packages/simplesoft.mediator.microsoft.extensions.validationpipeline&#34;&gt;pipeline available via NuGet&lt;/a&gt; that should be configurable to most use cases while also providing a simpler way to register the validators into the container.&lt;/p&gt;
&lt;p&gt;Install &lt;code&gt;SimpleSoft.Mediator.Microsoft.Extensions.ValidationPipeline&lt;/code&gt; via NuGet:&lt;/p&gt;
&lt;img src=&#34;/2020/11/04/Validation-with-Mediator-Pipelines-in-ASP-NET-Core-Applications/04_nuget_mediator_validation_pipeline.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;Use the extension method &lt;code&gt;AddPipelineForValidation&lt;/code&gt; and enforce both command and event validations and use the extension method &lt;code&gt;AddValidatorsFromAssemblyOf&lt;/code&gt; to scan for all &lt;code&gt;IValidator&amp;lt;T&amp;gt;&lt;/code&gt; classes and register them into the container.&lt;/p&gt;
&lt;p&gt;For the current project, the &lt;code&gt;ConfigureServices&lt;/code&gt; method would be similar to the following:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Startup&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ConfigureServices&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IServiceCollection services&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddMvc();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddSwaggerGen();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddDbContext&amp;lt;ApiDbContext&amp;gt;(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.UseInMemoryDatabase(&lt;span class=&#34;string&#34;&gt;&amp;quot;ApiDbContext&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddMediator(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;LoggingPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;TimeoutPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipelineForValidation(options =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                options.ValidateCommand = &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                options.ValidateEvent = &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddValidatorsFromAssemblyOf&amp;lt;Startup&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddHandlersFromAssemblyOf&amp;lt;Startup&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;I hope this article gave you a good idea on how to use mediator pipelines to ensure that all your commands, events and even queries are initialized with proper data, either implementing your own pipeline or by using the existing &lt;a href=&#34;https://www.nuget.org/packages/simplesoft.mediator.microsoft.extensions.validationpipeline&#34;&gt;ValidationPipeline&lt;/a&gt; NuGet.&lt;/p&gt;
&lt;p&gt;I also made an article about &lt;a href=&#34;/2020/11/07/Transaction-Management-With-Mediator-Pipelines-in-ASP-NET-Core/&#34; title=&#34;Transaction Management With Mediator Pipelines in ASP.NET Core&#34;&gt;transaction management with pipelines&lt;/a&gt;, you may also find it helpful.&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="aspnetcore" />
        <category term="csharp" />
        <category term="patterns" />
        <updated>2020-11-04T00:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://code-corner.dev/2020/11/02/Using-Mediator-Pipelines-in-ASP-NET-Core-Applications/</id>
        <title>Using Mediator Pipelines in ASP.NET Core Applications</title>
        <link rel="alternate" href="https://code-corner.dev/2020/11/02/Using-Mediator-Pipelines-in-ASP-NET-Core-Applications/"/>
        <content type="html">&lt;p&gt;In my &lt;a href=&#34;/2020/10/28/Mediator-Pattern-in-ASP-NET-Core-Applications/&#34; title=&#34;Mediator Pattern in ASP.NET Core Applications&#34;&gt;previous article&lt;/a&gt; I explained how you could implement &lt;strong&gt;Command Query Responsibility Segregation (CQRS)&lt;/strong&gt; and &lt;strong&gt;Event Sourcing (ES)&lt;/strong&gt; patterns using a mediator instance from &lt;a href=&#34;https://www.nuget.org/packages/simplesoft.mediator&#34;&gt;SimpleSoft.Mediator&lt;/a&gt; in ASP.NET Core applications.&lt;/p&gt;
&lt;p&gt;This time I’m going to explain how to intercept commands, queries and events, making it possible to apply transversal behavior before they reach the handlers, reducing significantly the amount of boilerplate and duplicated code.&lt;/p&gt;
&lt;p&gt;Transaction management, logging, auditing, caching or validations are some of the good examples where pipelines can be very helpful. Imagine the following chains:&lt;/p&gt;
&lt;figure class=&#34;highlight text&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;Commands:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  Mediator&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    LoggingPipeline&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      ValidationPipeline&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        TransactionPipeline&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          AuditPipeline&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Handler&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;Queries:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  Mediator&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    LoggingPipeline&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      Handler&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;Events:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  Mediator&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    LoggingPipeline&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      StoragePipeline&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Handler(s)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Not only you can plugin any pipeline at will without changing the handlers implementation, you can also be specific to the entities each pipeline is interested.&lt;/p&gt;
&lt;h1 id=&#34;Pipelines&#34;&gt;&lt;a href=&#34;#Pipelines&#34; class=&#34;headerlink&#34; title=&#34;Pipelines&#34;&gt;&lt;/a&gt;Pipelines&lt;/h1&gt;&lt;p&gt;Mediator pipelines are represented by the interface &lt;code&gt;IPipeline&lt;/code&gt; (or the abstract class &lt;code&gt;Pipeline&lt;/code&gt;) with methods to intercept each entity type:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;IPipeline&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;Task &lt;span class=&#34;title&#34;&gt;OnCommandAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TCommand&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        Func&amp;lt;TCommand, CancellationToken, Task&amp;gt; next, &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        TCommand cmd, &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TCommand : &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt;, ICommand&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;OnCommandAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TCommand&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        Func&amp;lt;TCommand, CancellationToken, Task&amp;lt;TResult&amp;gt;&amp;gt; next, &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        TCommand cmd, &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TCommand : &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;ICommand&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;Task &lt;span class=&#34;title&#34;&gt;OnEventAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TEvent&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        Func&amp;lt;TEvent, CancellationToken, Task&amp;gt; next, &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        TEvent evt, &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TEvent : &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt;, IEvent&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;OnQueryAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TQuery&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        Func&amp;lt;TQuery, CancellationToken, Task&amp;lt;TResult&amp;gt;&amp;gt; next, &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        TQuery query, &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TQuery : &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt;, IQuery&amp;lt;TResult&amp;gt;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;You receive a delegate for the next pipe and the entity, so the concept is very simple: either do some work before or after invoking the next pipe, or break the chain by returning or throwing an exception.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;IsItMondayAlreadyPipeline&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;Pipeline&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;override&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;OnQueryAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TQuery&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        Func&amp;lt;TQuery, CancellationToken, Task&amp;lt;TResult&amp;gt;&amp;gt; next, &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        TQuery query, &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;        CancellationToken ct&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;function&#34;&gt;    &lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt;(DateTime.Now.DayOfWeek == DayOfWeek.Monday)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; InvalidOperationException(&lt;span class=&#34;string&#34;&gt;&amp;quot;Today is monday, no data for you!&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(query, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;Pipelines are executed by the same order they are added to the container so, to be deterministic, don’t scan the assemblies for classes implementing &lt;code&gt;IPipeline&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&#34;Commands-pipeline&#34;&gt;&lt;a href=&#34;#Commands-pipeline&#34; class=&#34;headerlink&#34; title=&#34;Commands pipeline&#34;&gt;&lt;/a&gt;Commands pipeline&lt;/h2&gt;&lt;p&gt;Intercept commands sent into the mediator by implementing the methods &lt;code&gt;OnCommandAsync&lt;/code&gt;:&lt;/p&gt;
&lt;img src=&#34;/2020/11/02/Using-Mediator-Pipelines-in-ASP-NET-Core-Applications/01_pipeline_command.png&#34; class=&#34;&#34;&gt;

&lt;h2 id=&#34;Queries-pipeline&#34;&gt;&lt;a href=&#34;#Queries-pipeline&#34; class=&#34;headerlink&#34; title=&#34;Queries pipeline&#34;&gt;&lt;/a&gt;Queries pipeline&lt;/h2&gt;&lt;p&gt;Intercept queries being fetched from the mediator by implementing the methods &lt;code&gt;OnQueryAsync&lt;/code&gt;:&lt;/p&gt;
&lt;img src=&#34;/2020/11/02/Using-Mediator-Pipelines-in-ASP-NET-Core-Applications/02_pipeline_query.png&#34; class=&#34;&#34;&gt;

&lt;h2 id=&#34;Events-pipeline&#34;&gt;&lt;a href=&#34;#Events-pipeline&#34; class=&#34;headerlink&#34; title=&#34;Events pipeline&#34;&gt;&lt;/a&gt;Events pipeline&lt;/h2&gt;&lt;p&gt;Intercept events before being broadcasted by the mediator into all the handlers by implementing the method &lt;code&gt;OnEventAsync&lt;/code&gt;:&lt;/p&gt;
&lt;img src=&#34;/2020/11/02/Using-Mediator-Pipelines-in-ASP-NET-Core-Applications/03_pipeline_event.png&#34; class=&#34;&#34;&gt;

&lt;hr&gt;
&lt;h1 id=&#34;The-project&#34;&gt;&lt;a href=&#34;#The-project&#34; class=&#34;headerlink&#34; title=&#34;The project&#34;&gt;&lt;/a&gt;The project&lt;/h1&gt;&lt;p&gt;To make it easier, we are going to leverage on the &lt;a href=&#34;/2020/10/28/Mediator-Pattern-in-ASP-NET-Core-Applications/&#34; title=&#34;Mediator Pattern in ASP.NET Core Applications&#34;&gt;previous article&lt;/a&gt; and continue from there. As a reminder, we implemented an endpoint to manage products with the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GET &amp;#x2F;products — search for products using some filters (&lt;code&gt;SearchProductsQuery&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;GET &amp;#x2F;products&amp;#x2F;{id} — get a product by its unique identifier (&lt;code&gt;GetProductByIdQuery&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;POST &amp;#x2F;products — create a product (&lt;code&gt;CreateProductCommand&lt;/code&gt; and &lt;code&gt;CreatedProductEvent&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;PUT &amp;#x2F;products&amp;#x2F;{id} — update a product by its unique identifier (&lt;code&gt;UpdateProductCommand&lt;/code&gt; and &lt;code&gt;UpdatedProductEvent&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;DELETE &amp;#x2F;products&amp;#x2F;{id} — delete a product by its unique identifier (&lt;code&gt;DeleteProductCommand&lt;/code&gt; and &lt;code&gt;DeletedProductEvent&lt;/code&gt;);&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The complete source code can be found on &lt;a href=&#34;https://github.com/gravity00/IntroductionMediatorCQRS&#34;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;The-pipelines&#34;&gt;&lt;a href=&#34;#The-pipelines&#34; class=&#34;headerlink&#34; title=&#34;The pipelines&#34;&gt;&lt;/a&gt;The pipelines&lt;/h1&gt;&lt;p&gt;Since the objective of this article is to show how to implement pipelines in the mediator, the following should be enough:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LoggingPipeline&lt;/strong&gt; — serializes every command, queries, events and results as JSON into the log if &lt;code&gt;Trace&lt;/code&gt; is enabled;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TimeoutPipeline&lt;/strong&gt; — cancels the handler execution if it takes more than a given set of time;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Start by creating a &lt;code&gt;Pipelines&lt;/code&gt; folder at the project root level.&lt;/p&gt;
&lt;img src=&#34;/2020/11/02/Using-Mediator-Pipelines-in-ASP-NET-Core-Applications/04_project_structure_pipelines.png&#34; class=&#34;&#34;&gt;

&lt;h2 id=&#34;LoggingPipeline&#34;&gt;&lt;a href=&#34;#LoggingPipeline&#34; class=&#34;headerlink&#34; title=&#34;LoggingPipeline&#34;&gt;&lt;/a&gt;LoggingPipeline&lt;/h2&gt;&lt;p&gt;Because we don’t want logging to affect the timeout, this will be the first pipeline to be added to the chain.&lt;/p&gt;
&lt;p&gt;Inside the &lt;code&gt;Pipelines&lt;/code&gt; folder, create a &lt;code&gt;LoggingPipeline&lt;/code&gt; class implementing &lt;code&gt;IPipeline&lt;/code&gt;. This pipeline will receive an &lt;code&gt;ILogger&lt;/code&gt; instance and use &lt;code&gt;System.Text.Json&lt;/code&gt; to serialize the objects:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;LoggingPipeline&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IPipeline&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; ILogger&amp;lt;LoggingPipeline&amp;gt; _logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; JsonSerializerOptions _serializerOptions = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; JsonSerializerOptions&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        WriteIndented = &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;LoggingPipeline&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;ILogger&amp;lt;LoggingPipeline&amp;gt; logger&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        _logger = logger;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;OnCommandAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TCommand&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TCommand, CancellationToken, Task&amp;gt; next, TCommand cmd, CancellationToken ct&lt;/span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TCommand : &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt;, ICommand&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Log(&lt;span class=&#34;string&#34;&gt;&amp;quot;Command: &amp;#123;command&amp;#125;&amp;quot;&lt;/span&gt;, cmd);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(cmd, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;OnCommandAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TCommand&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TCommand, CancellationToken, Task&amp;lt;TResult&amp;gt;&amp;gt; next, TCommand cmd, CancellationToken ct&lt;/span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TCommand : &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt;, ICommand&amp;lt;TResult&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Log(&lt;span class=&#34;string&#34;&gt;&amp;quot;Command: &amp;#123;command&amp;#125;&amp;quot;&lt;/span&gt;, cmd);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; result = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(cmd, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Log(&lt;span class=&#34;string&#34;&gt;&amp;quot;Command.Result: &amp;#123;commandResult&amp;#125;&amp;quot;&lt;/span&gt;, result);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; result;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;OnEventAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TEvent&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TEvent, CancellationToken, Task&amp;gt; next, TEvent evt, CancellationToken ct&lt;/span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TEvent : &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt;, IEvent&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Log(&lt;span class=&#34;string&#34;&gt;&amp;quot;Event: &amp;#123;event&amp;#125;&amp;quot;&lt;/span&gt;, evt);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(evt, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;OnQueryAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TQuery&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TQuery, CancellationToken, Task&amp;lt;TResult&amp;gt;&amp;gt; next, TQuery query, CancellationToken ct&lt;/span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TQuery : &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt;, IQuery&amp;lt;TResult&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Log(&lt;span class=&#34;string&#34;&gt;&amp;quot;Query: &amp;#123;query&amp;#125;&amp;quot;&lt;/span&gt;, query);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; result = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(query, ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        Log(&lt;span class=&#34;string&#34;&gt;&amp;quot;Query.Result: &amp;#123;queryResult&amp;#125;&amp;quot;&lt;/span&gt;, result);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; result;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Log&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;string&lt;/span&gt; message, T instance&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (_logger.IsEnabled(LogLevel.Trace))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            _logger.LogTrace(message, JsonSerializer.Serialize(instance, _serializerOptions));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Open the &lt;code&gt;Startup.cs&lt;/code&gt; file and register the pipeline into the mediator:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;services.AddMediator(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    o.AddPipeline&amp;lt;LoggingPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    o.AddHandlersFromAssemblyOf&amp;lt;Startup&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Open the &lt;code&gt;appsettings.development.json&lt;/code&gt; file and set the default log level to &lt;code&gt;Trace&lt;/code&gt;:&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;Logging&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;LogLevel&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;span class=&#34;attr&#34;&gt;&amp;quot;Default&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;Trace&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;span class=&#34;attr&#34;&gt;&amp;quot;Microsoft&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;Warning&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;span class=&#34;attr&#34;&gt;&amp;quot;Microsoft.Hosting.Lifetime&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;Information&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you now start the server and do some actions in the API, like creating or searching for products, you should start seeing your objects being serialized:&lt;/p&gt;
&lt;img src=&#34;/2020/11/02/Using-Mediator-Pipelines-in-ASP-NET-Core-Applications/05_pipeline_logging_logs.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;&lt;em&gt;As a note, if you find this helpful for your APIs, you can use the NuGet &lt;a href=&#34;https://www.nuget.org/packages/simplesoft.mediator.microsoft.extensions.loggingpipeline&#34;&gt;SimpleSoft.Mediator.Microsoft.Extensions.LoggingPipeline&lt;/a&gt;. Just give a look at the &lt;code&gt;Startup.cs&lt;/code&gt; file in &lt;a href=&#34;https://github.com/simplesoft-pt/Mediator/blob/master/work/SimpleSoft.Mediator.Example.Api/Startup.cs&#34;&gt;the example API&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&#34;TimeoutPipeline&#34;&gt;&lt;a href=&#34;#TimeoutPipeline&#34; class=&#34;headerlink&#34; title=&#34;TimeoutPipeline&#34;&gt;&lt;/a&gt;TimeoutPipeline&lt;/h2&gt;&lt;p&gt;Inside the &lt;code&gt;Pipelines&lt;/code&gt; folder, create a &lt;code&gt;TimeoutPipeline&lt;/code&gt; class implementing &lt;code&gt;IPipeline&lt;/code&gt;. For simplicity, the timeout value will be fixed, but could be received as an options from the container.&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;TimeoutPipeline&lt;/span&gt; : &lt;span class=&#34;title&#34;&gt;IPipeline&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;readonly&lt;/span&gt; TimeSpan Timeout = TimeSpan.FromMilliseconds(&lt;span class=&#34;number&#34;&gt;500&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;OnCommandAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TCommand&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TCommand, CancellationToken, Task&amp;gt; next, TCommand cmd, CancellationToken ct&lt;/span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TCommand : &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt;, ICommand&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; cts = CreateCancellationTokenSource(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(cmd, cts.Token);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;OnCommandAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TCommand&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TCommand, CancellationToken, Task&amp;lt;TResult&amp;gt;&amp;gt; next, TCommand cmd, CancellationToken ct&lt;/span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TCommand : &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt;, ICommand&amp;lt;TResult&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; cts = CreateCancellationTokenSource(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(cmd, cts.Token);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; Task &lt;span class=&#34;title&#34;&gt;OnEventAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TEvent&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TEvent, CancellationToken, Task&amp;gt; next, TEvent evt, CancellationToken ct&lt;/span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TEvent : &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt;, IEvent&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; cts = CreateCancellationTokenSource(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(evt, cts.Token);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Task&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt; &lt;span class=&#34;title&#34;&gt;OnQueryAsync&lt;/span&gt;&amp;lt;&lt;span class=&#34;title&#34;&gt;TQuery&lt;/span&gt;, &lt;span class=&#34;title&#34;&gt;TResult&lt;/span&gt;&amp;gt;(&lt;span class=&#34;params&#34;&gt;Func&amp;lt;TQuery, CancellationToken, Task&amp;lt;TResult&amp;gt;&amp;gt; next, TQuery query, CancellationToken ct&lt;/span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;function&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;where&lt;/span&gt; TQuery : &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt;, IQuery&amp;lt;TResult&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; cts = CreateCancellationTokenSource(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; next(query, cts.Token);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;static&lt;/span&gt; CancellationTokenSource &lt;span class=&#34;title&#34;&gt;CreateCancellationTokenSource&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;CancellationToken ct&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;var&lt;/span&gt; cts = CancellationTokenSource.CreateLinkedTokenSource(ct);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        cts.CancelAfter(Timeout);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; cts;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Once again, open the &lt;code&gt;Startup.cs&lt;/code&gt; file and register the pipeline into the mediator &lt;strong&gt;after&lt;/strong&gt; the logging pipe:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;services.AddMediator(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    o.AddPipeline&amp;lt;LoggingPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    o.AddPipeline&amp;lt;TimeoutPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    o.AddHandlersFromAssemblyOf&amp;lt;Startup&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you now open any of your handlers and add an &lt;code&gt;Task.Delay(1000, ct)&lt;/code&gt; into the &lt;code&gt;HandleAsync&lt;/code&gt; methods you should receive a &lt;code&gt;TaskCanceledException&lt;/code&gt;. I added one when searching for products:&lt;/p&gt;
&lt;img src=&#34;/2020/11/02/Using-Mediator-Pipelines-in-ASP-NET-Core-Applications/06_pipeline_timeout_stacktrace_response.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;If you look at the stack trace, you’ll see the invocation order was kept:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ProductsController&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    MicrosoftFetcher (internal class used by the mediator)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        LoggingPipeline&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            TimeoutPipeline&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                SearchProductsQueryHandler&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The project structure with pipeline classes:&lt;/p&gt;
&lt;img src=&#34;/2020/11/02/Using-Mediator-Pipelines-in-ASP-NET-Core-Applications/07_project_structure_pipelines_impl.png&#34; class=&#34;&#34;&gt;

&lt;p&gt;The final &lt;code&gt;Startup.cs&lt;/code&gt; file:&lt;/p&gt;
&lt;figure class=&#34;highlight cs&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Startup&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;ConfigureServices&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IServiceCollection services&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddMvc();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddSwaggerGen();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddDbContext&amp;lt;ApiDbContext&amp;gt;(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.UseInMemoryDatabase(&lt;span class=&#34;string&#34;&gt;&amp;quot;ApiDbContext&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        services.AddMediator(o =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;LoggingPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddPipeline&amp;lt;TimeoutPipeline&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            o.AddHandlersFromAssemblyOf&amp;lt;Startup&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;Configure&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;IApplicationBuilder app, IWebHostEnvironment env&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.UseDeveloperExceptionPage();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.UseSwagger();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.UseSwaggerUI(c =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            c.SwaggerEndpoint(&lt;span class=&#34;string&#34;&gt;&amp;quot;/swagger/v1/swagger.json&amp;quot;&lt;/span&gt;, &lt;span class=&#34;string&#34;&gt;&amp;quot;Mediator ExampleApi V1&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.UseRouting();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        app.UseEndpoints(endpoints =&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            endpoints.MapControllers();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h1 id=&#34;Conclusion&#34;&gt;&lt;a href=&#34;#Conclusion&#34; class=&#34;headerlink&#34; title=&#34;Conclusion&#34;&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;&lt;p&gt;I hope this article gave you a good idea about mediator pipelines and how you can use them to empower your solutions, making them more resilient to developer mistakes (like forgetting to save changes into the database).&lt;/p&gt;
&lt;p&gt;For me, this is one of the strongest aspects of the mediator pattern and one of the main reason I always use this pattern even when implementing &lt;strong&gt;minimum viable products&lt;/strong&gt;.&lt;/p&gt;
</content>
        <category term="dotnetcore" />
        <category term="aspnetcore" />
        <category term="csharp" />
        <category term="patterns" />
        <updated>2020-11-02T00:00:00.000Z</updated>
    </entry>
</feed>
