<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Kyle Oettlé Blog</title>
        <link>https://your-docusaurus-test-site.com/blog</link>
        <description>Kyle Oettlé Blog</description>
        <lastBuildDate>Mon, 22 Sep 2025 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Targeted Feature Flags with Azure App Configuration and Custom ITargetingContextAccessor]]></title>
            <link>https://your-docusaurus-test-site.com/blog/targeted-feature-flags</link>
            <guid>https://your-docusaurus-test-site.com/blog/targeted-feature-flags</guid>
            <pubDate>Mon, 22 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Hi Everyone]]></description>
            <content:encoded><![CDATA[<p>Hi Everyone <!-- -->👋</p>
<p>We've all had to roll out some form of feature management, whether it was static values in an appsettings.json file or a fully managed service with feature management based on various rules.</p>
<p>In this post we'll be unpacking the Azure App Configuration Feature Management a little bit, using a Targeted filter and creating a custom <code>ITargetingContextAccessor</code> implementation.</p>
<blockquote>
<p>You can check out my <a href="https://github.com/kyleoettle/feature_flags" target="_blank" rel="noopener noreferrer">feature_flags repository</a> and play around if you want.<br>
<!-- -->It contains all the code examples for this post.<br>
<!-- -->The repo is deployable to Azure, just call <code>azd up</code> or run it locally if you have an App Configuration resource already.</p>
</blockquote>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="introduction">Introduction<a href="https://your-docusaurus-test-site.com/blog/targeted-feature-flags#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction">​</a></h2>
<p>Azure has had App Configuration available for quite some time now, and I knew it provided Feature Management out of the box, but I wanted to see if it could provide a more dynamic experience than a Feature On/Off switch, or a complex json blob with a custom filter.</p>
<p>I had a little dive into the <a href="https://learn.microsoft.com/en-us/azure/azure-app-configuration/concept-feature-management" target="_blank" rel="noopener noreferrer">App Configuration Feature Management Documentation</a> and was pleasantly surprised with the Targeted filter type.</p>
<blockquote>
<p>When I say dynamic experience - I tried to see if I could manage feature flags at multiple levels, let's say enable it for certain users, as well as certain groups (or companies, modules, countries, you get it...)</p>
</blockquote>
<p>The Azure App Configuration Feature Flag with a Targeted filter allowed me exactly that.</p>
<ul>
<li>A simple but meaningful UI that allowed me to visually enable or disable features for certain users or groups.</li>
<li>RBAC to allow fine grained control on who can make changes to the feature flags</li>
<li><a href="https://learn.microsoft.com/en-us/azure/azure-app-configuration/howto-best-practices?tabs=dotnet#configuration-refresh" target="_blank" rel="noopener noreferrer">Configurable refresh intervals</a> based on my application's needs</li>
<li>An easy to implement Interface to allow custom feature evaluation</li>
</ul>
<p>Obviously there are loads more benefits, these were the top 4 requirements I wanted to tick off in this post.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-start">The start<a href="https://your-docusaurus-test-site.com/blog/targeted-feature-flags#the-start" class="hash-link" aria-label="Direct link to The start" title="Direct link to The start">​</a></h2>
<p>Microsoft has some really good <a href="https://learn.microsoft.com/en-us/azure/azure-app-configuration/howto-targetingfilter" target="_blank" rel="noopener noreferrer">documentation</a> on how to get started with Feature Flags and Targeted filters.</p>
<p><strong>Microsoft describes it as a strategy that you can use to progressively roll out new features by targeting a known group of uniquely identifiable entities.</strong></p>
<p>You can read up more about it in the documentation, but I wanted to share an important part of how this filter work - the evaluation flow.</p>
<p><img decoding="async" loading="lazy" alt="Azure App Configuration Targeted Filter Evaluation Flow" src="https://your-docusaurus-test-site.com/assets/images/evaluation-flow-c8a6c692876ba058a8e083ddc1cb3b9b.png" width="611" height="1007" class="img_ev3q"></p>
<p>This shows you how the users and groups you configure within the Targeted filter will be evaluated.<br>
<!-- -->This was really cool to see and slightly more than I was expecting!</p>
<p>I don't see myself needing a feature flag where I want to exclude users, exclude groups, Include users, include groups, and have a percentage weight to it.<br>
<!-- -->My requirements stopped at</p>
<ul>
<li><code>on for everyone, but exclude these users and groups</code> and</li>
<li><code>off for everyone, but include these users and groups</code></li>
</ul>
<p>Next is the Default Percentage slider - this is great because you can enable a feature, but set the default percentage of entities that it's on for.<br>
<!-- -->This matches my need of a feature being enabled - but on for everyone by default, or off for everyone by default</p>
<p><img decoding="async" loading="lazy" alt="Default Percentage Slider for Targeted Filter" src="https://your-docusaurus-test-site.com/assets/images/default-percentage-b007774b16d3e9e3db6ccdec178c5885.png" width="957" height="493" class="img_ev3q"></p>
<p><strong>Configuring Services</strong></p>
<p>Okay let's finally dive into my code and see how I configured everything.</p>
<div class="language-CSharp language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> builder </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> WebApplication</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">CreateBuilder</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Retrieve the endpoint</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token class-name keyword" style="color:#00009f">string</span><span class="token plain"> endpoint </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> builder</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Configuration</span><span class="token punctuation" style="color:#393A34">.</span><span class="token generic-method function" style="color:#d73a49">GetValue</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&lt;</span><span class="token generic-method generic class-name keyword" style="color:#00009f">string</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Endpoints:AppConfiguration"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">??</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">InvalidOperationException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"The setting `Endpoints:AppConfiguration` was not found."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Load configuration from Azure App Configuration </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">builder</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Configuration</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">AddAzureAppConfiguration</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">options </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    options</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Connect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">Uri</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">endpoint</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">DefaultAzureCredential</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    options</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">UseFeatureFlags</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">options </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        options</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">SetRefreshInterval</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TimeSpan</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">FromSeconds</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">30</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">builder</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Services</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">AddFeatureManagement</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WithTargeting</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">//register the AzureAppConfigurationRefresherProvider services that's required for automatic refresh.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">builder</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Services</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">AddAzureAppConfiguration</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>This looks pretty standard and matches what the documentation provided. <code>So why the long blog post?</code></p>
<p>You can see I added the Azure App Configuration and set the RefreshInterval.<br>
<!-- -->Although the default is 30 seconds anyway, I like to set thes values explicitly.<br>
<!-- -->It creates visibility for myself or the team - since not everyone has the same context.</p>
<p>When initially testing it out it wasn't obvious to me yet, but <code>builder.Services.AddFeatureManagement()</code> registered the services as Singleton services. More about it later.</p>
<p>The <code>builder.Services.AddAzureAppConfiguration()</code> ensures the RefreshProvider is loaded which will be required by the middleware that I register next.</p>
<p><strong>Middleware and endpoints</strong></p>
<div class="language-CSharp language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> app </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> builder</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Build</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">//middleware that will call refresh based on the refresh interval</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">UseAzureAppConfiguration</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">MapGet</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/feature-flag"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">IFeatureManager</span><span class="token plain"> featureManager</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> featureManager</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">IsEnabledAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Demo"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Results</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Ok</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Feature Demo is enabled for you"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Results</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Ok</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Feature Demo is disabled for you"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">MapGet</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/enabled-flags"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">IFeatureManagerSnapshot</span><span class="token plain"> featureManager</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> enabledFeatures </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">List</span><span class="token constructor-invocation class-name punctuation" style="color:#393A34">&lt;</span><span class="token constructor-invocation class-name keyword" style="color:#00009f">string</span><span class="token constructor-invocation class-name punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">foreach</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> feature </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> featureManager</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">GetFeatureNamesAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> featureManager</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">IsEnabledAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">feature</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            enabledFeatures</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">feature</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Results</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Ok</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">enabledFeatures</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>The <code>app.UseAzureAppConfiguration();</code> added the middleware that will actually refresh the feature flag values.</p>
<blockquote>
<p>Interestingly enough, this doesn't mean that the feature flags will refresh every 30 seconds (or on your Refresh Interval) - it actually just means that, at most, it will refresh every 30 seconds.<br>
<!-- -->It can refresh at a much slower rate if there is no traffic and the middleware isn't triggered.<br>
<!-- -->This prevents the service from blasting the App Configuration unnecessary and slowing down your services.<br>
<!-- -->You can also remove the middleware and refresh the feature flags on demand.</p>
</blockquote>
<p>The first endpoint <code>/feature-flag</code> checks if I'm enabled for the <code>Demo</code> feature. Pretty simple.</p>
<p>The second endpoint <code>/enabled-flags</code> will return all the feature flags I'm enabled for.<br>
<!-- -->This was only for testing purposes. It's probably better than checking each feature flag independently over numerous network calls, but could also be really heavy if I have a large amount of feature flags and complex or slow evaluation logic.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-challenges">The challenges<a href="https://your-docusaurus-test-site.com/blog/targeted-feature-flags#the-challenges" class="hash-link" aria-label="Direct link to The challenges" title="Direct link to The challenges">​</a></h2>
<ul>
<li>Out of the box, the Targeted filter comes with a <a href="https://github.com/microsoft/FeatureManagement-Dotnet/blob/main/src/Microsoft.FeatureManagement.AspNetCore/DefaultHttpTargetingContextAccessor.cs" target="_blank" rel="noopener noreferrer">default Context Accessor</a> that works by utilizing the <code>HttpContext.User.Identity.Name</code> as the <code>UserId</code> and the <code>HttpContext.User.Claims</code> of type Role for <code>Groups</code> - This is great if you're using Entra ID or other common Identity Providers.<br>
<!-- -->But often Authentication and Authorization is split, or you want to roll out features based on some other user related value like location, age, net worth, etc.</li>
<li>Case sensitive evaluation: Turns out the Targeted filter is case sensitive by default. I'm not a fan and I'm not dealing with the headache of debugging <code>test@test.com</code> vs <code>Test@test.com</code></li>
<li>Singleton services: The Feature Management services are added as Singleton services, so it might be a problem if you want to use scoped services in your custom implementation.<br>
<!-- -->It does make sense why it's registered as Singleton services, feature flags are "configuration" and shouldn't change per request, Singletons are more performant to load and it will improve consistency (if you have a high amount of traffic, scoped services might end up having different values - although you shouldn't be relying on feature flags if you need that level of consistency in my opinion)</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-solution">The solution<a href="https://your-docusaurus-test-site.com/blog/targeted-feature-flags#the-solution" class="hash-link" aria-label="Direct link to The solution" title="Direct link to The solution">​</a></h2>
<p>I'll start by addressing the case sensitive evaluation.</p>
<p>If you want to have case insensitive evaluation, you need to configure the <code>TargetingEvaluationOptions</code>. Easy as that.</p>
<div class="language-CSharp language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">//case insensitive comparison for FeatureManager.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">builder</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Services</span><span class="token punctuation" style="color:#393A34">.</span><span class="token generic-method function" style="color:#d73a49">Configure</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&lt;</span><span class="token generic-method generic class-name">TargetingEvaluationOptions</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">options </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    options</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">IgnoreCase </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>If you need to use Scoped services for your FeatureManagement, you can change your service registration to the following:</p>
<div class="language-CSharp language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">builder</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Services</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">AddScopedFeatureManagement</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WithTargeting</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>Now my <code>IFeatureManager</code> is registered as a Scoped service and can use other Scoped services.</p>
<blockquote>
<p>Just a note on the Scoped vs Singleton services, if I call <code>await featureManager.IsEnabledAsync("Demo")</code> twice, it will evaluate the feature flag twice. There is no magic in the Feature Manager that will cache the result or anything differently between the Scoped vs Singleton instances.</p>
</blockquote>
<p><strong>Custom feature flag evaluation using <code>ITargetingContextAccessor</code></strong><br>
<!-- -->Now this was the interesting part.<br>
<!-- -->Creating your own implementation of the interface allows you to inject any context you want to evaluate the user or groups parameters.</p>
<p>The <a href="https://github.com/microsoft/FeatureManagement-Dotnet/blob/main/src/Microsoft.FeatureManagement/Targeting/ITargetingContextAccessor.cs" target="_blank" rel="noopener noreferrer">ITargetingContextAccessor</a> Interface is straight forward and has a single method <code>ValueTask&lt;TargetingContext&gt; GetContextAsync();</code><br>
<!-- -->The <a href="https://github.com/microsoft/FeatureManagement-Dotnet/blob/main/src/Microsoft.FeatureManagement/Targeting/TargetingContext.cs" target="_blank" rel="noopener noreferrer">TargetingContext</a> class has two properties to represent the UserId and a list of groups.</p>
<p>For this example, I create a UserService that will return some user related information, more specifically, companies I have access to.</p>
<div class="language-CSharp language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">UserService</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token type-list class-name">IUserService</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">//Get user related information</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token return-type class-name">User</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">GetCurrentUser</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> groups </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">List</span><span class="token constructor-invocation class-name punctuation" style="color:#393A34">&lt;</span><span class="token constructor-invocation class-name">Companies</span><span class="token constructor-invocation class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">Companies</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">Guid</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"A9726B81-F306-450B-B26B-0EB061700633"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"CompanyA"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">Companies</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">Guid</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"5A1230AD-48FC-461A-B44F-1D3477974664"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"CompanyB"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">Companies</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">Guid</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"835DAB0A-8378-44D2-8F93-9FC49D3D7849"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"CompanyX"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">Guid</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"41EAA244-9939-48D4-9820-B0B41F716F81"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"MockUser"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> groups</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">record</span><span class="token plain"> </span><span class="token class-name">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">Guid</span><span class="token plain"> ID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name keyword" style="color:#00009f">string</span><span class="token plain"> Name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name">List</span><span class="token class-name punctuation" style="color:#393A34">&lt;</span><span class="token class-name">Companies</span><span class="token class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> Companies</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">record</span><span class="token plain"> </span><span class="token class-name">Companies</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">Guid</span><span class="token plain"> ID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name keyword" style="color:#00009f">string</span><span class="token plain"> Name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>I then created my custom implementation of the <code>ITargetingContextAccessor</code> interface</p>
<div class="language-CSharp language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">DemoTargetingContextAccessor</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token type-list class-name">ITargetingContextAccessor</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">readonly</span><span class="token plain"> </span><span class="token class-name">IUserService</span><span class="token plain"> _userService</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">DemoTargetingContextAccessor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">IUserService</span><span class="token plain"> userService</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        _userService </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> userService</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token return-type class-name">ValueTask</span><span class="token return-type class-name punctuation" style="color:#393A34">&lt;</span><span class="token return-type class-name">TargetingContext</span><span class="token return-type class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">GetContextAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> user </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> _userService</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">GetCurrentUser</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> targetingContext </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">TargetingContext</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            UserId </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> user</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Name</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">ToString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            Groups </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> user</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Companies</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Select</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">x </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> x</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Name</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">ToString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">ValueTask</span><span class="token constructor-invocation class-name punctuation" style="color:#393A34">&lt;</span><span class="token constructor-invocation class-name">TargetingContext</span><span class="token constructor-invocation class-name punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">targetingContext</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>In this example I used the User Name and Company Name values to populate the TargetingContext - so in my Targeted filter in the App Configuration I can enter nice human readable values like CompanyX, or Kyle, etc.<br>
<!-- -->I could have easily used the ID or any other values, and configured the Targeted filter accordingly.</p>
<p>Below is an example where I registered a Targeted filter with the <strong>Default Percentage</strong> set to 0 - so by default off for everyone, and I added <code>mockuser</code> to the <strong>Include Users</strong> - so this filter is only enabled for the single user.</p>
<p><img decoding="async" loading="lazy" alt="Example Targeted Filter Feature Flag Configuration with User filter" src="https://your-docusaurus-test-site.com/assets/images/example-filter-da943ff02b49ca090025697a673c7736.png" width="968" height="595" class="img_ev3q">
Lastly, I registered the new services and I was done!</p>
<div class="language-CSharp language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">builder</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Services</span><span class="token punctuation" style="color:#393A34">.</span><span class="token generic-method function" style="color:#d73a49">AddScoped</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&lt;</span><span class="token generic-method generic class-name">IUserService</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">,</span><span class="token generic-method generic class-name"> UserService</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">builder</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Services</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">AddScopedFeatureManagement</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token generic-method function" style="color:#d73a49">WithTargeting</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&lt;</span><span class="token generic-method generic class-name">DemoTargetingContextAccessor</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>To test the feature management, I configured a few different features:
<img decoding="async" loading="lazy" alt="Example Targeted Filter Feature Flag Configurations" src="https://your-docusaurus-test-site.com/assets/images/demo-filters-2e317ab2c3233e2ffe477580a8dc1308.png" width="1499" height="434" class="img_ev3q"></p>
<ul>
<li>Feature <code>Demo1</code> enabled for my <code>mockuser</code></li>
<li>Feature <code>Demo2</code> enabled for <code>companyz</code></li>
<li>Feature <code>Demo3</code> disabled for <code>mockuser</code> but enabled for <code>companyb</code></li>
<li>Feature <code>Demo4</code> enabled with an empty Target filter but 100% allocation</li>
</ul>
<p>And here is the response from my <code>/enabled-flags</code> endpoint
<img decoding="async" loading="lazy" alt="Enabled Feature Flags Endpoint Response" src="https://your-docusaurus-test-site.com/assets/images/enabled-flags-e93e6f614953f3a813e1bd6888962a22.png" width="790" height="164" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="some-interesting-bits">Some interesting bits<a href="https://your-docusaurus-test-site.com/blog/targeted-feature-flags#some-interesting-bits" class="hash-link" aria-label="Direct link to Some interesting bits" title="Direct link to Some interesting bits">​</a></h2>
<p>Remember earlier when I said that feature flags will always be evaluated every time when calling <code>await featureManager.IsEnabledAsync("Demo")</code> ?</p>
<p>The <a href="https://github.com/microsoft/FeatureManagement-Dotnet/blob/main/src/Microsoft.FeatureManagement.AspNetCore/DefaultHttpTargetingContextAccessor.cs" target="_blank" rel="noopener noreferrer">DefaultHttpTargetingContextAccessor</a> comes with a really nice pattern where it caches the result for the duration of the request.  A similar optimization should be considered if you want to create your own implementation.</p>
<div class="language-CSharp language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token class-name">HttpContext</span><span class="token plain"> httpContext </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> _httpContextAccessor</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">HttpContext</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Try cache lookup</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">httpContext</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Items</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">TryGetValue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">_cacheKey</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">out</span><span class="token plain"> </span><span class="token class-name keyword" style="color:#00009f">object</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">value</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">ValueTask</span><span class="token constructor-invocation class-name punctuation" style="color:#393A34">&lt;</span><span class="token constructor-invocation class-name">TargetingContext</span><span class="token constructor-invocation class-name punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TargetingContext</span><span class="token punctuation" style="color:#393A34">)</span><span class="token keyword" style="color:#00009f">value</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// rest of the code</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Cache for subsequent lookup</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">httpContext</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Items</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">_cacheKey</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> targetingContext</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>When overriding the users in the Targeting filter, there is a warning that feature flags have a 10kb limit.</p>
<p><img decoding="async" loading="lazy" alt="Azure App Configuration 10kb Feature Flag Limit Warning" src="https://your-docusaurus-test-site.com/assets/images/10kb-warning-e4b179a892c88fb612883ccd6dab3219.png" width="936" height="640" class="img_ev3q"></p>
<p>It's not really clear from that message if it only applies to filtering on users, or what exactly falls under that limit for the Targeted filter, and the same message doesn't appear anywhere else on the filter - but looking at the <a href="https://learn.microsoft.com/en-us/azure/azure-app-configuration/faq?#are-there-any-size-limitations-on-keys-and-values-stored-in-app-configuration" target="_blank" rel="noopener noreferrer">App Configuration FAQs</a> it looks like it's a limit for a single key-value, which makes me believe it's for an entire "Feature Flag"</p>
<p>Looking at the structure for a Feature Flag we can do some simple math to calculate how many values you can filter on with a 10kb limit.</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token property" style="color:#36acaa">"id"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"demo"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token property" style="color:#36acaa">"description"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token property" style="color:#36acaa">"enabled"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token property" style="color:#36acaa">"conditions"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		</span><span class="token property" style="color:#36acaa">"client_filters"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				</span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Microsoft.Targeting"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				</span><span class="token property" style="color:#36acaa">"parameters"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">					</span><span class="token property" style="color:#36acaa">"Audience"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">						</span><span class="token property" style="color:#36acaa">"Users"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">							</span><span class="token string" style="color:#e3116c">"User1"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">							</span><span class="token string" style="color:#e3116c">"User2"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">						</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">						</span><span class="token property" style="color:#36acaa">"Groups"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">							</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">								</span><span class="token property" style="color:#36acaa">"Name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"CompanyA"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">								</span><span class="token property" style="color:#36acaa">"RolloutPercentage"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">							</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">							</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">								</span><span class="token property" style="color:#36acaa">"Name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"CompanyB"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">								</span><span class="token property" style="color:#36acaa">"RolloutPercentage"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">							</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">						</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">						</span><span class="token property" style="color:#36acaa">"DefaultRolloutPercentage"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">					</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>Let's assume you have really long names and a user filter uses 50 characters and a group filter uses 100 characters.</p>
<p>Each user = 50 characters (plus quotes and commas). Let’s round to 55 bytes per user.<br>
<!-- -->10240 / 55 = 186 users</p>
<p>Each group = 100 characters (plus JSON overhead like <code>{"Name":"...","RolloutPercentage":100}</code>). That’s closer to 130 bytes per group.<br>
<!-- -->10240 / 130 = 78 groups</p>
<p>That's still a good number of users or groups that you can apply the filter to even if they have reeeeally long names.<br>
<!-- -->With the rest of the json data in the structure I would be cautious going over 75 groups or 180 users - but you do the math</p>
<hr>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="try-it-out">Try it out<a href="https://your-docusaurus-test-site.com/blog/targeted-feature-flags#try-it-out" class="hash-link" aria-label="Direct link to Try it out" title="Direct link to Try it out">​</a></h3>
<p><em>Want to play around?
You can check out my <a href="https://github.com/kyleoettle/feature_flags" target="_blank" rel="noopener noreferrer">feature_flags repository</a> to get started.<br>
<!-- -->It contains all the code examples for this post.<br>
<!-- -->The repo is deployable to Azure, just call <code>azd up</code> or run it locally if you already have an App Configuration resource.</em></p>]]></content:encoded>
            <category>Azure</category>
            <category>Feature Flags</category>
            <category>App Configuration</category>
            <category>.NET</category>
            <category>ITargetingContextAccessor</category>
            <category>azd</category>
        </item>
        <item>
            <title><![CDATA[Burnout lessons from swimming]]></title>
            <link>https://your-docusaurus-test-site.com/blog/burnout-lessons</link>
            <guid>https://your-docusaurus-test-site.com/blog/burnout-lessons</guid>
            <pubDate>Fri, 12 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Hi Everyone]]></description>
            <content:encoded><![CDATA[<p>Hi Everyone <!-- -->👋</p>
<p>I want to share a lesson I learned about burnout, from my sister's swimming!</p>
<p>We've all felt it, stretched too thin, juggling too many things, pushing too hard - Burnout is tricky, it creeps up on you, and by the time your realize it, you're already drained.</p>
<p>Burnout can happen for a bunch of different reasons, at work you might be wearing too many different hats and trying to fill too many gaps, entering a stressful period or working, taking on too much work for an unrealistic deadline. It can even be as simple as you've just been working too long without a break.<br>
<!-- -->At home you might have personal circumstances that require more of you than usual, stressful life events, physical changes or stressors - we've all experienced what a few consecutive days of bad sleep can do to us 😂</p>
<p>My sister told me a story of how her swim training made the proverbial penny drop on how burnout can look without you even noticing it.</p>
<p>The lesson...</p>
<p>My sister joined a swim club a while ago, during one particular session, as they approached the back end - they had to do 4 slow laps, 4 medium laps and end with 4 laps swimming full out.<br>
<!-- -->Great, she thought, little bit of planning and she can smash this!<br>
<!-- -->She was swimming along nicely in the first 4 laps, conserving her energy, feeling good.<br>
<!-- -->The next 4 laps she started pushing a bit, she felt like she was in her sweet spot, getting a bit tired but manageable, it's only 4 laps, how tired could it make her?<br>
<!-- -->Next was the hard laps, she pushed her hardest, it felt like she was giving everything, kicking extra hard and being ultra focused on her effort, her technique, her breathing, she was absolutely smashing it - or so she thought.</p>
<p>Afterwards when she compared her times, her last 4 laps when she gave her everything - were slower than her first 4 slow laps 😦</p>
<p>This is when burnout truly made sense to her - a few minutes in a pool painted a picture that we often don't see in the workplace because burnout doesn't usually happen overnight, it's days, weeks or months.<br>
<!-- -->What happened was she was working at a maintainable pace, she was happy, stressors were normal but increasing. As she pushed harder, she had to keep on pushing harder, she gave her best but by the end her best was worse than her barely trying. That's the thing about burnout, it takes so much more from you to deal with the basics, nevermind performing well.</p>
<p>Her story really made me think and reflect on my own experiences (and sometimes bad habits)</p>
<p>I remember starting a new project, and every little bump in the road upset me, I started to take it personally, pushed even harder and it got to a point where it affected me at home, my sleep, my physical health. I took a week off, focused on myself, enjoying every day and spending quality time with my family. When I got back to work, nothing burned down, the project was still on track, some of the same bumps were still there - but they were easy to deal with and no problem at all 😂</p>
<p>I'd like to leave you with this.</p>
<p>Have you been feeling frustrated, your goal always just out of reach, like all you need to do is push a little bit harder, <code>and this is the 3rd time you're just pushing harder</code>?
Have you considered that maybe you're on your last 4 laps and you're burned out?</p>
<p>Sometimes the best way forward isn't pushing harder, it's taking a break.</p>]]></content:encoded>
            <category>Burnout</category>
            <category>Lessons</category>
            <category>Balance</category>
        </item>
        <item>
            <title><![CDATA[The Evolution of Console Apps - How AI Agents Could Transform Software Engineering]]></title>
            <link>https://your-docusaurus-test-site.com/blog/ai-agent-console-app</link>
            <guid>https://your-docusaurus-test-site.com/blog/ai-agent-console-app</guid>
            <pubDate>Fri, 08 Aug 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Hi Everyone]]></description>
            <content:encoded><![CDATA[<p>Hi Everyone <!-- -->👋</p>
<p>I built something cool! <a href="https://github.com/kyleoettle/agents" target="_blank" rel="noopener noreferrer">AI Agents repository</a> - an AI agent that performs automated code reviews.</p>
<p>Like everyone, I've been playing with AI Agents for a while now. I'm lucky enough that Investec allows, and encourages, us to adopt AI and use it to our advantage. But I've always thought there should be more to it. I wanted to see the 10x it promised.</p>
<p>Recently, I've been challenging myself whether I'm only using AI where I'm encouraged to use it - like GitHub Copilot - or if we can change our perspective and use it to impact the entire team or organisation.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-evolution-of-the-console-app">The Evolution of the Console App<a href="https://your-docusaurus-test-site.com/blog/ai-agent-console-app#the-evolution-of-the-console-app" class="hash-link" aria-label="Direct link to The Evolution of the Console App" title="Direct link to The Evolution of the Console App">​</a></h2>
<p>Almost everyone reading this has probably built a console app - some probably hundreds of console apps <!-- -->😀</p>
<p>I've been thinking about how <strong>Agents could be the next evolution of console apps we build.</strong></p>
<p>I don't mean using Agents to just search for certain keywords in a log file or build a CSV. Those are perfectly suited to a Console App. I mean in the sense that it should take me minutes or just a few hours to automate a very manual, repetitive, organization-wide task.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-agent-pr-experiment">The Agent PR Experiment<a href="https://your-docusaurus-test-site.com/blog/ai-agent-console-app#the-agent-pr-experiment" class="hash-link" aria-label="Direct link to The Agent PR Experiment" title="Direct link to The Agent PR Experiment">​</a></h2>
<p>I decided to build an AI agent to challenge my thinking: <strong>Could I automate a code review?</strong></p>
<p><code>"But Kyle, you love doing code reviews, it's part of what you enjoy about your job" - Yes and No! I love doing a code review to provide valuable feedback, it's also how I get to share my knowledge, but this also takes up a lot of time, and you don't always have time to do a code review when someone wants one.</code></p>
<p>The result is an <a href="https://github.com/kyleoettle/agents" target="_blank" rel="noopener noreferrer">AI Agent that can perform a code review and leave feedback</a>, a Multi-Agent workflow that performs comprehensive code reviews on pull requests from GitHub.</p>
<p>In the link I shared, I spent a few short hours spinning up a basic (and I mean really basic) Agent in Python that can perform a code review. But the results were pretty encouraging!</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="building-the-agent-with-the-same-freedom-as-building-a-console-app---fast-and-furious">Building the Agent: With the same freedom as building a console app - fast and furious!<a href="https://your-docusaurus-test-site.com/blog/ai-agent-console-app#building-the-agent-with-the-same-freedom-as-building-a-console-app---fast-and-furious" class="hash-link" aria-label="Direct link to Building the Agent: With the same freedom as building a console app - fast and furious!" title="Direct link to Building the Agent: With the same freedom as building a console app - fast and furious!">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="the-architecture">The Architecture<a href="https://your-docusaurus-test-site.com/blog/ai-agent-console-app#the-architecture" class="hash-link" aria-label="Direct link to The Architecture" title="Direct link to The Architecture">​</a></h3>
<p><img decoding="async" loading="lazy" alt="child-in-parent" src="https://your-docusaurus-test-site.com/assets/images/agent-flow-d3a60f5fde13e816aa6ce829cbe039f0.svg" width="811" height="1043" class="img_ev3q">
I tried to keep the design simple and let the agent evolve to fit my needs, no real pre-planning.<br>
<!-- -->I know with AI Agents that having multiple agents focus on smaller sub tasks outperforms a single agent trying to do everything.</p>
<p><strong>⭐ Multi-Agent Review System (async tasks)</strong><br>
<!-- -->I ended up with three different agents so that they could focus on their specific tasks:</p>
<ul>
<li><strong>Style Reviewer</strong>: The one that catches all the indentation and naming stuff that makes me go <!-- -->😒</li>
<li><strong>Security Reviewer</strong>: Uses OWASP Top 10 to spot the scary stuff</li>
<li><strong>Summarizer</strong>: Takes all the feedback and makes it actually useful</li>
</ul>
<p>I figured this would be enough to see if I could get valuable feedback from the workflow, and identify any improvements I'd want to make in the future.</p>
<p><strong>⭐ StateGraph (Static Dictionary)</strong></p>
<blockquote>
<p>LangGraph is a Python framework for building stateful, multi-agent workflows.<br>
<!-- -->StateGraph enables the state management and execution flow.</p>
</blockquote>
<p>I really enjoyed spending time on thinking about this process, using LangGraph's StateGraph, and how I would use this in a "real world" scenario.<br>
<!-- -->The interesting part was that just using in-memory state was good enough, and that the agent flow required was really simple.<br>
<!-- -->This agent is supposed to execute fast, doesn't require any async execution or human in the loop.<br>
<!-- -->A pity, I would have loved to throw in CosmosDB to store the state and some complex agent flows.</p>
<p>One of the concepts I wanted to test was to see if I could make the reviews more meaningful by passing in an instruction file from the repository linked to the code review.<br>
<!-- -->Like mentioned above, I just stored the the state in memory - and as agents completed I stored their state in the same object to be used by the Summarizer agent.
<img decoding="async" loading="lazy" alt="child-in-parent" src="https://your-docusaurus-test-site.com/assets/images/state-flow-8667b0630051155f5537f40982ee4068.svg" width="1032" height="1217" class="img_ev3q"></p>
<p><strong>⭐ Local development &amp; cloud deployment</strong><br>
<!-- -->When you build a console app, you run it locally, but the truly special ones might make it to a different environment, so I wanted to test both, and for this scenario it made sense to support cloud deployments - it wouldn't make sense to run an agent locally for code reviews.</p>
<ul>
<li><strong>Local Development</strong>: Ollama with phi4-mini - free and fast for getting started</li>
<li><strong>Cloud Deployment</strong>: Azure OpenAI with GPT-4o - to be honest, this was the model that came out of the box with the azd template so why not.</li>
</ul>
<p><strong>⭐ Secure~ish Design</strong><br>
<!-- -->Security comes out of the box for any cloud platforms and I made use of the basics</p>
<ul>
<li><strong>Azure Key Vault</strong> for secrets</li>
<li><strong>Managed Identity</strong> to access Azure resources</li>
</ul>
<p>Some of the things I didn't do:</p>
<ul>
<li><strong>Api authentication</strong> - Allowing unauthorized users to call your api is 99% of times a very bad idea. It opens up the api to spamming innocent pull requests with code reviews <code>But Kyle, you can trust strangers on the internet, no?</code> and in the process leaving you with a huge bill and embarrassment. This being a POC, Authentication wasn't something that I wanted to prove, so I skipped it for now.</li>
<li><strong>Prompt injection protection</strong> - and this is where I should probably spend some more time. Due to the design of the agent, it tries to read a "pr_instructions.md" file from the repo it's about to review for custom instructions. My first thought was that this should be okay, considering you're reviewing your own code, you would only be injecting yourself - but security is only as strong as your weakest link and if a malicious user got access to the PR agent, it could probably do some real damage.<br>
<!-- -->Here is an example of a "pr_instructions.md" file I used to give my reviewers more context.<br>
<img decoding="async" loading="lazy" alt="child-in-parent" src="https://your-docusaurus-test-site.com/assets/images/pr_instructions-d53d45cf03ee8071fcdb1e43efbee6f1.png" width="1276" height="936" class="img_ev3q"></li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="what-i-learned">What I learned<a href="https://your-docusaurus-test-site.com/blog/ai-agent-console-app#what-i-learned" class="hash-link" aria-label="Direct link to What I learned" title="Direct link to What I learned">​</a></h3>
<p>What really surprised me while building the agent was how easy it was to get <em>something</em> up and running really quickly.<br>
<!-- -->And this probably isn't specific to Python and LangGraph, it could have been C# and Semantic Kernel, or AWS instead of Azure.  Much like a console app, these tools all feel well supported, easy to use and fit right into your current ecosystem.<br>
<!-- -->I didn't have to dig deep or search far to get something working, all I had to do was try.</p>
<p>Some more AI Agent related lessons that I learned:</p>
<ul>
<li><strong>All LLMs aren't created equal</strong> - There are real differences in their responses, even with just the default settings. To get real value out of an AI Agent, you probably need to tweak its settings a bit.</li>
<li><strong>Your agents can be more specific than you think</strong> - While I was creating this proof of concept I kept on thinking about adding more instructions or context to an agent, and the thoughts quickly turned to "what different agents could I add". I'm lucky that I work with some incredibly smart engineers, and maybe I could try and mimic some of them, instead of a more generic "Security Reviewer" agent.</li>
<li><strong>Fine tuning your prompts really matter</strong> - I played with a few different prompts and they evolved over time. I wish I saved my first and last prompt (or code reviews) to show the difference. From a complete failure to something providing real value.<br>
<img decoding="async" loading="lazy" alt="child-in-parent" src="https://your-docusaurus-test-site.com/assets/images/pr_review-33ed56ae9972453be4264a3b58482e9b.png" width="913" height="860" class="img_ev3q"></li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="whats-next-for-my-code-review-agent">What's next for my code review agent.<a href="https://your-docusaurus-test-site.com/blog/ai-agent-console-app#whats-next-for-my-code-review-agent" class="hash-link" aria-label="Direct link to What's next for my code review agent." title="Direct link to What's next for my code review agent.">​</a></h3>
<ul>
<li><strong>Improving the context with function calling</strong> - When I do a code review I often look at the rest of the file to gain more context. Loading all the files into the prompt from the start seems like a bad idea, but using function calling to load specific files if they require more context might be a great way of allowing the Agents to perform better code reviews.</li>
<li><strong>Recreating a team member</strong> - One idea I want to test is recreating one of our engineers by loading all of their previous code reviews into a Vector Database and using Semantic Search to retreive specific reviews based on the engineer and the file, and exposing it to the Agent. If we want value out of these code reviews, how about giving it the context of the best reviewers in the team.</li>
</ul>
<blockquote>
<p>Vector Database is a type of database that stores text or code as numbers so a computer can quickly find things with similar meaning.<br>
<!-- -->Semantic Serach is a search method that finds results based on meaning and context rather than just keyword matches.</p>
</blockquote>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="final-thoughts">Final Thoughts<a href="https://your-docusaurus-test-site.com/blog/ai-agent-console-app#final-thoughts" class="hash-link" aria-label="Direct link to Final Thoughts" title="Direct link to Final Thoughts">​</a></h3>
<p>Most organisations have had to migrate from one code repository to another, from GitHub to Azure DevOps, from BitBucket to GitHub, from SVN to where ever.<br>
<!-- -->Most organisations also had to rebuild their build and release pipelines from... you get the picture.</p>
<p>Along with code reviews, these are all very repeatable tasks that follow the same patterns and in my opinion are ideal for AI Agents.<br>
<!-- -->If you're challenged with rebuilding 200 pipelines, will it be worth spending 10% of the time creating an Agent and letting it do all the hard work?</p>
<p>I think I proved to some degree that AI Agents can be a valuable tool in the everyday developer playbook.
It takes a bit of time to set up your first agent, the next one is faster, the third one feels like a real console app.<br>
<!-- -->For organisations that require more governance, I can see setting up a sandbox environment or template with built-in auditing and security being a good way to enable the organisations to iterate faster in a safe manner.</p>
<hr>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="try-it-out">Try it out<a href="https://your-docusaurus-test-site.com/blog/ai-agent-console-app#try-it-out" class="hash-link" aria-label="Direct link to Try it out" title="Direct link to Try it out">​</a></h3>
<p><em>Want to dive deeper? Check out the <a href="https://github.com/kyleoettle/agents" target="_blank" rel="noopener noreferrer">AI Agents repository</a> and let me know what you build! I'd love to hear about your experiments.
The repo is deployable to Azure. Just call <code>azd up</code> or run it locally with Ollama.</em></p>]]></content:encoded>
            <category>AI</category>
            <category>Agents</category>
            <category>LangGraph</category>
            <category>Python</category>
            <category>Code Review</category>
            <category>DevOps</category>
            <category>Azure</category>
            <category>azd</category>
        </item>
        <item>
            <title><![CDATA[BudgetSpeech - I made something!]]></title>
            <link>https://your-docusaurus-test-site.com/blog/budgetspeech-introduction</link>
            <guid>https://your-docusaurus-test-site.com/blog/budgetspeech-introduction</guid>
            <pubDate>Fri, 08 Aug 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Hi Everyone]]></description>
            <content:encoded><![CDATA[<p>Hi Everyone <!-- -->👋</p>
<p>I made something cool! <a href="https://app.budgetspeech.com/" target="_blank" rel="noopener noreferrer">budgetspeech</a></p>
<p>I love personal finance, sometimes it can be hard, sometimes it can be stressful, but it's something all of us have to deal with in some capacity.</p>
<p>My wife and I have a bi-weekly meeting called <!-- -->⭐<!-- --> <b>Budget Speech </b> <!-- -->⭐<!-- --> - Where either of us get to be the Minister of Finance for the evening and, you know, give the Budget Speech :satisfied:</p>
<p>We have a big Excel document with all our budgets for different expenses and we track it throughout the month. This workes pretty great and we can be really specific about which expenses gets allocated against which category. This way we're always on the same page whether we're getting McDonalds on Friday, or if we should rather stick to the leftovers in the fridge.</p>
<p>Here is a small snippit of what the excel document used to look like
<img decoding="async" loading="lazy" alt="child-in-parent" src="https://your-docusaurus-test-site.com/assets/images/budgetspeech-excel-20fe543bd0f66ccb02e0dd70bd2264db.png" width="314" height="290" class="img_ev3q"></p>
<p>There are a few issues though...</p>
<ul>
<li>You have to download your bank statements manually. We share a single bank account for expenses and only one of us can receive the In App Authentication request to log in. So only one of us has access to the statements.</li>
<li>You can't download the statements from the Investec Mobile App, so you need to use a laptop.</li>
<li>Every now and then we create a copy of the Excel document or save it in a different location. You would think two <del>smart</del> people could keep track of a single document on a shared drive...</li>
<li>It's still a very manual process you need to dedicate time for.</li>
<li>It's hard to keep track of your historical trends and you need to flip through Excel sheets</li>
<li>I am a fan of <a href="https://www.vault22.io/" target="_blank" rel="noopener noreferrer">Vault22</a> (formerly 22Seven) but it never really did what I wanted. When you go to Checkers you might buy food for yourself and dog food. They aren't both "groceries". One is "pets"</li>
<li>It's boring and I love coding.</li>
</ul>
<p>This is where <a href="https://app.budgetspeech.com/" target="_blank" rel="noopener noreferrer">budgetspeech</a> comes in.</p>
<p>I bank with <a href="https://www.investec.com/" target="_blank" rel="noopener noreferrer">Investec</a> and they support <a href="https://www.investec.com/en_gb/welcome-to-investec/digital/open-banking.html" target="_blank" rel="noopener noreferrer">Open Banking</a>, and in South Africa they have a similar set of api's based on the same standard called <a href="https://www.investec.com/en_za/banking/tech-professionals/programmable-banking.html" target="_blank" rel="noopener noreferrer">Programmable Banking</a></p>
<p>I decided to have fun, over engineer and play around while automating my Excel version of BudgetSpeech.<br>
<!-- -->Read along the next couple of episodes as I discuss what and how I built <a href="https://app.budgetspeech.com/" target="_blank" rel="noopener noreferrer">budgetspeech</a>.</p>
<p>In the video below I used <a href="https://developer.investec.com/za/api-products/documentation/SA_PB_Account_Information#section/Authentication" target="_blank" rel="noopener noreferrer">Investec's sandbox environment and credentials</a>. You're welcome to play around with it or just create a <code>Mock Account</code> - no bank account required <!-- -->😉</p>
<video width="640" height="480" controls=""><source src="/img/blog-images/budgetspeech-introduction/budgetspeech-create-account.mp4" type="video/mp4"></video>]]></content:encoded>
            <category>Programmable Banking</category>
            <category>Open Banking</category>
            <category>BudgetSpeech</category>
            <category>Investec</category>
        </item>
        <item>
            <title><![CDATA[Fun with (Enum) Flags]]></title>
            <link>https://your-docusaurus-test-site.com/blog/enum-flags</link>
            <guid>https://your-docusaurus-test-site.com/blog/enum-flags</guid>
            <pubDate>Wed, 08 Nov 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Hi Everyone]]></description>
            <content:encoded><![CDATA[<p>Hi Everyone <!-- -->👋</p>
<p>One of the lesser known but my favorite features of Enums are <a href="https://learn.microsoft.com/en-us/dotnet/api/system.flagsattribute?view=net-7.0" target="_blank" rel="noopener noreferrer">Flags</a></p>
<p>Enums are usually used to indicate a single option, like a day of the week, your favorite colour, pizza size... We all get it, we've all used it, but how often have you seen an array of Enums being passed around in your code?<br>
<!-- -->There was probably a good reason for it if you have. Maybe an array of features, an array of preferred contact methods, supported payment methods?  This is what's really cool about Flags, You can capture a selection of enums in a single value :satisfied:</p>
<p>Here is the code in my <a href="https://github.com/kyleoettle/example-enum-flags" target="_blank" rel="noopener noreferrer">Github repo</a> that you can check out.</p>
<p>Lets create an enum to store a list of client preferences.</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">[</span><span class="token attribute class-name">Flags</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">enum</span><span class="token plain"> </span><span class="token class-name">ClientPreferences</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    None </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    DarkMode </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    LargeText </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    AutoComplete </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    AIAssist </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    AutoOrder </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>Things to note is that I added the <code>[Flags]</code> attribute to the enum, and I set the values to powers of 2 and a default <code>None = 0</code> enum.
This way I can combine the enum values to create a single value that represents a combination of preferences.</p>
<p>Lets create a variable containing some of our preferences and print them out.</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> preferences </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ClientPreferences</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">LargeText </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> ClientPreferences</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">AutoComplete </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> ClientPreferences</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">AutoOrder</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WriteLine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token interpolation-string string" style="color:#e3116c">$"Preferences String value: </span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">{</span><span class="token interpolation-string interpolation expression language-csharp">preferences</span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">}</span><span class="token interpolation-string string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WriteLine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token interpolation-string string" style="color:#e3116c">$"Preferences Integer value: </span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">{</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">(</span><span class="token interpolation-string interpolation expression language-csharp keyword" style="color:#00009f">int</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">)</span><span class="token interpolation-string interpolation expression language-csharp">preferences</span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">}</span><span class="token interpolation-string string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>Because we selected LargeText, AutoComplete and AutoOrder, the output will be:</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Preferences String value: LargeText, AutoComplete, AutoOrder</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Preferences Integer value: 22</span><br></span></code></pre></div></div>
<p>The Integer value is 22 because we added the values of the selected preferences together. This is great because there is no other combination of preferences that will add up to 14!</p>
<p>Using the HasFlag Function, we can check if a preference is selected in a combination of preferences.<br>
<!-- -->Lets print out our preferences to see the HasFlag values.</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WriteLine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token interpolation-string string" style="color:#e3116c">$"Preferences HasFlag:None: </span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">{</span><span class="token interpolation-string interpolation expression language-csharp">preferences</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp function" style="color:#d73a49">HasFlag</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">(</span><span class="token interpolation-string interpolation expression language-csharp">ClientPreferences</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp">None</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">)</span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">}</span><span class="token interpolation-string string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WriteLine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token interpolation-string string" style="color:#e3116c">$"Preferences HasFlag:DarkMode: </span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">{</span><span class="token interpolation-string interpolation expression language-csharp">preferences</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp function" style="color:#d73a49">HasFlag</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">(</span><span class="token interpolation-string interpolation expression language-csharp">ClientPreferences</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp">DarkMode</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">)</span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">}</span><span class="token interpolation-string string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WriteLine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token interpolation-string string" style="color:#e3116c">$"Preferences HasFlag:LargeText: </span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">{</span><span class="token interpolation-string interpolation expression language-csharp">preferences</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp function" style="color:#d73a49">HasFlag</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">(</span><span class="token interpolation-string interpolation expression language-csharp">ClientPreferences</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp">LargeText</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">)</span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">}</span><span class="token interpolation-string string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WriteLine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token interpolation-string string" style="color:#e3116c">$"Preferences HasFlag:AutoComplete: </span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">{</span><span class="token interpolation-string interpolation expression language-csharp">preferences</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp function" style="color:#d73a49">HasFlag</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">(</span><span class="token interpolation-string interpolation expression language-csharp">ClientPreferences</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp">AutoComplete</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">)</span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">}</span><span class="token interpolation-string string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WriteLine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token interpolation-string string" style="color:#e3116c">$"Preferences HasFlag:AIAssist: </span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">{</span><span class="token interpolation-string interpolation expression language-csharp">preferences</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp function" style="color:#d73a49">HasFlag</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">(</span><span class="token interpolation-string interpolation expression language-csharp">ClientPreferences</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp">AIAssist</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">)</span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">}</span><span class="token interpolation-string string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WriteLine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token interpolation-string string" style="color:#e3116c">$"Preferences HasFlag:AutoOrder: </span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">{</span><span class="token interpolation-string interpolation expression language-csharp">preferences</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp function" style="color:#d73a49">HasFlag</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">(</span><span class="token interpolation-string interpolation expression language-csharp">ClientPreferences</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp">AutoOrder</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">)</span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">}</span><span class="token interpolation-string string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>The output will be:</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Preferences HasFlag:None: True</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Preferences HasFlag:DarkMode: False</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Preferences HasFlag:LargeText: True</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Preferences HasFlag:AutoComplete: True</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Preferences HasFlag:AIAssist: False</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Preferences HasFlag:AutoOrder: True</span><br></span></code></pre></div></div>
<blockquote>
<p>You'll notice the None enum value is also printed out. This is because the None enum value is 0, and 0 is included in all combinations of preferences. If you don't want a None enum option, you can ommit it and start your index at 1.</p>
</blockquote>
<p><code>"But Kyle, I want to get an array of preferences, not a single value! Please Help!"</code></p>
<p>You can still get an array of enum values if you want to.</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> selectedPreferences </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    System</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Enum</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">GetValues</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">typeof</span><span class="token punctuation" style="color:#393A34">(</span><span class="token type-expression class-name">ClientPreferences</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token generic-method function" style="color:#d73a49">Cast</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&lt;</span><span class="token generic-method generic class-name">ClientPreferences</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Where</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> preferences</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">HasFlag</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>And printing out the array, the output will be:</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Preferences in Array: None</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Preferences in Array: LargeText</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Preferences in Array: AutoComplete</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Preferences in Array: AutoOrder</span><br></span></code></pre></div></div>]]></content:encoded>
            <category>c#</category>
            <category>.net</category>
            <category>Enum</category>
            <category>FlagsAttribute</category>
        </item>
        <item>
            <title><![CDATA[Async (a)void]]></title>
            <link>https://your-docusaurus-test-site.com/blog/async-void</link>
            <guid>https://your-docusaurus-test-site.com/blog/async-void</guid>
            <pubDate>Sat, 04 Nov 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Hi Everyone]]></description>
            <content:encoded><![CDATA[<p>Hi Everyone <!-- -->👋</p>
<p>A while ago we had the unfortunate event of breaking prod <!-- -->😮<br>
<!-- -->We made some small changes to our .net api, all tests passed, everything was good and the world slept peacefully that night, until we saw the service started going down seemingly at random! Turns out it wasn't random at all, it was a pesky async void which we missed changing to an async Task and it caused the entire service to come crumbling down.</p>
<p>Most of us know that when you want to change a method from sync to async, you change the calls to an async Task, it's pretty simple and straight forward, but we forgot to change one of the signatures from <code>void DoSomething()</code> to <code>async Task DoSomething()</code> and left it as <code>async void DoSomething()</code></p>
<p>"But Kyle, surely in an api the controller will just rethrow the error, right?" - Yes and no!<br>
<!-- -->The problem is in the way that Tasks and voids propagate their exceptions, and the way we didn't await our call.<br>
<!-- -->When an exception is thrown in an async Task, you can await the Task and the exception is captured in the Task's context, even if you don't await the task and it's "fire and forget", the exception is still captured in an anonymous Task's context.<br>
<!-- -->With async void, there is no return type, it's always "fire and forget" and there is no place to capture the exception.<br>
<!-- -->In short it causes an <a href="https://learn.microsoft.com/en-us/dotnet/standard/threading/exceptions-in-managed-threads?redirectedfrom=MSDN" target="_blank" rel="noopener noreferrer">exception in the ThreadPool which causes the application to crash</a></p>
<p>Let's look at some examples in my <a href="https://github.com/kyleoettle/async-void-example" target="_blank" rel="noopener noreferrer">github repo</a> to explain some behaviour.</p>
<p>I have a controller with 3 methods to demonstrate the different scenarios</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">[</span><span class="token attribute class-name">Route</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">(</span><span class="token attribute attribute-arguments string" style="color:#e3116c">"[controller]/[action]"</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">[</span><span class="token attribute class-name">ApiController</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">DemoController</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token type-list class-name">ControllerBase</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">readonly</span><span class="token plain"> </span><span class="token class-name">IDemoService</span><span class="token plain"> demoService</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">static</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">readonly</span><span class="token plain"> </span><span class="token class-name keyword" style="color:#00009f">string</span><span class="token class-name punctuation" style="color:#393A34">[</span><span class="token class-name punctuation" style="color:#393A34">]</span><span class="token plain"> demoValues </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"A"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"B"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"C"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">DemoController</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">IDemoService</span><span class="token plain"> demoService</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">demoService </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> demoService</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">[</span><span class="token attribute class-name">HttpGet</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">(</span><span class="token attribute attribute-arguments">Name </span><span class="token attribute attribute-arguments operator" style="color:#393A34">=</span><span class="token attribute attribute-arguments"> </span><span class="token attribute attribute-arguments string" style="color:#e3116c">"GetTask"</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token return-type class-name">Task</span><span class="token return-type class-name punctuation" style="color:#393A34">&lt;</span><span class="token return-type class-name">IEnumerable</span><span class="token return-type class-name punctuation" style="color:#393A34">&lt;</span><span class="token return-type class-name keyword" style="color:#00009f">string</span><span class="token return-type class-name punctuation" style="color:#393A34">&gt;</span><span class="token return-type class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">GetTask</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// fire and forget</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// no await so exception will be caught in TaskScheduler.UnobservedTaskException</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// demoValues will be returned</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        demoService</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">PerformTaskAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> demoValues</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">[</span><span class="token attribute class-name">HttpGet</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">(</span><span class="token attribute attribute-arguments">Name </span><span class="token attribute attribute-arguments operator" style="color:#393A34">=</span><span class="token attribute attribute-arguments"> </span><span class="token attribute attribute-arguments string" style="color:#e3116c">"GetTaskAsync"</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token return-type class-name">Task</span><span class="token return-type class-name punctuation" style="color:#393A34">&lt;</span><span class="token return-type class-name">IEnumerable</span><span class="token return-type class-name punctuation" style="color:#393A34">&lt;</span><span class="token return-type class-name keyword" style="color:#00009f">string</span><span class="token return-type class-name punctuation" style="color:#393A34">&gt;</span><span class="token return-type class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">GetAsyncTask</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// awaits demoService.PerformTaskAsync</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// exception will be thrown and handled in ExceptionMiddleware</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// demoValues will not be returned</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> demoService</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">PerformTaskAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> demoValues</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">[</span><span class="token attribute class-name">HttpGet</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">(</span><span class="token attribute attribute-arguments">Name </span><span class="token attribute attribute-arguments operator" style="color:#393A34">=</span><span class="token attribute attribute-arguments"> </span><span class="token attribute attribute-arguments string" style="color:#e3116c">"GetVoidAsync"</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token return-type class-name">Task</span><span class="token return-type class-name punctuation" style="color:#393A34">&lt;</span><span class="token return-type class-name">IEnumerable</span><span class="token return-type class-name punctuation" style="color:#393A34">&lt;</span><span class="token return-type class-name keyword" style="color:#00009f">string</span><span class="token return-type class-name punctuation" style="color:#393A34">&gt;</span><span class="token return-type class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">GetAsyncVoid</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// fire and forget</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// no await so exception will be logged in AppDomain.CurrentDomain.UnhandledException and crash</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// demoValues will be returned</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        demoService</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">PerformVoidAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> demoValues</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>I have custom middleware to handle exceptions and do some fancy stuff</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">ExceptionMiddleware</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">readonly</span><span class="token plain"> </span><span class="token class-name">RequestDelegate</span><span class="token plain"> _next</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">readonly</span><span class="token plain"> </span><span class="token class-name">ILogger</span><span class="token class-name punctuation" style="color:#393A34">&lt;</span><span class="token class-name">ExceptionMiddleware</span><span class="token class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> _logger</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ExceptionMiddleware</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">RequestDelegate</span><span class="token plain"> next</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name">ILogger</span><span class="token class-name punctuation" style="color:#393A34">&lt;</span><span class="token class-name">ExceptionMiddleware</span><span class="token class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> logger</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        _next </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> next</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        _logger </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> logger</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token return-type class-name">Task</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">InvokeAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">HttpContext</span><span class="token plain"> httpContext</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">try</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">_next</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">httpContext</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">Exception</span><span class="token plain"> exception</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic">// log the error</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            _logger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">LogError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">exception</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"error during executing {Context}"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> httpContext</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Request</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Path</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Value</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> response </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> httpContext</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Response</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            response</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ContentType </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"application/json"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic">//do some fancy error handling</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            response</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">StatusCode </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">int</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">HttpStatusCode</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">InternalServerError</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> message </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> System</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Text</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">JsonSerializer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Serialize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token interpolation-string string" style="color:#e3116c">$"Whoops, something went wrong! -  </span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">{</span><span class="token interpolation-string interpolation expression language-csharp">exception</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp">Message</span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">}</span><span class="token interpolation-string string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> response</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WriteAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">message</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>I have some extra event handlers to demonstrate the fire and forget exception behaviour</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">TaskScheduler</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">UnobservedTaskException </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sender</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WriteLine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token interpolation-string string" style="color:#e3116c">$"*** UnhandledException in TaskScheduler! - </span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">{</span><span class="token interpolation-string interpolation expression language-csharp">e</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp">Exception</span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">}</span><span class="token interpolation-string string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">AppDomain</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">CurrentDomain</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">UnhandledException </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sender</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WriteLine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token interpolation-string string" style="color:#e3116c">$"*** UnhandledException in AppDomain! - </span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">{</span><span class="token interpolation-string interpolation expression language-csharp">e</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp">ExceptionObject</span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">}</span><span class="token interpolation-string string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>And I have a demo service that will throw some errors in async calls</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">DemoService</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token type-list class-name">IDemoService</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token return-type class-name">Task</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">PerformTaskAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Delay</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">NotImplementedException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">nameof</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">PerformTaskAsync</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">PerformVoidAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Delay</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">NotImplementedException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">nameof</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">PerformVoidAsync</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="awaiting-async-task---caught-in-middleware">Awaiting async Task - Caught in middleware<a href="https://your-docusaurus-test-site.com/blog/async-void#awaiting-async-task---caught-in-middleware" class="hash-link" aria-label="Direct link to Awaiting async Task - Caught in middleware" title="Direct link to Awaiting async Task - Caught in middleware">​</a></h3>
<p>When you await a Task and an exception is thrown, the exception will be stored on the Task's context and the calling code will know how to handle it.</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">[</span><span class="token attribute class-name">HttpGet</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">(</span><span class="token attribute attribute-arguments">Name </span><span class="token attribute attribute-arguments operator" style="color:#393A34">=</span><span class="token attribute attribute-arguments"> </span><span class="token attribute attribute-arguments string" style="color:#e3116c">"GetTaskAsync"</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token return-type class-name">Task</span><span class="token return-type class-name punctuation" style="color:#393A34">&lt;</span><span class="token return-type class-name">IEnumerable</span><span class="token return-type class-name punctuation" style="color:#393A34">&lt;</span><span class="token return-type class-name keyword" style="color:#00009f">string</span><span class="token return-type class-name punctuation" style="color:#393A34">&gt;</span><span class="token return-type class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">GetAsyncTask</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// awaits demoService.PerformTaskAsync</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// exception will be thrown and handled in ExceptionMiddleware</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// demoValues will not be returned</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> demoService</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">PerformTaskAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> demoValues</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>In this case of calling the <code>GetTaskAsync</code> endpoint the middleware caught the error and you can see the custom error message <code>"Whoops, something went wrong!"</code> in the result.<br>
<!-- -->This is great and I guess in 99% of cases what you want.</p>
<p><img decoding="async" loading="lazy" alt="child-in-parent" src="https://your-docusaurus-test-site.com/assets/images/async-await-task-90abbb6b2eb7eab7bcd2fb321a02ad72.png" width="863" height="772" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="fire-and-forget-async-task---caught-in-taskschedulerunobservedtaskexception">Fire-and-forget async Task - Caught in TaskScheduler.UnobservedTaskException<a href="https://your-docusaurus-test-site.com/blog/async-void#fire-and-forget-async-task---caught-in-taskschedulerunobservedtaskexception" class="hash-link" aria-label="Direct link to Fire-and-forget async Task - Caught in TaskScheduler.UnobservedTaskException" title="Direct link to Fire-and-forget async Task - Caught in TaskScheduler.UnobservedTaskException">​</a></h3>
<p>When you don't await a Task and an exception is thrown, the exception is handled by the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskscheduler.unobservedtaskexception" target="_blank" rel="noopener noreferrer">TaskScheduler.UnobservedTaskException</a></p>
<blockquote>
<p>This event provides a mechanism to prevent exception escalation policy (which, by default, terminates the process) from triggering.</p>
</blockquote>
<p>This is great, so out of the box if a Task throws an exception and we didn't await it, it won't crash our process and we have the ability to log or react on it! One thing to remember is that the Task has to be collected by the Garbage Collector before the event is raised.</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">[</span><span class="token attribute class-name">HttpGet</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">(</span><span class="token attribute attribute-arguments">Name </span><span class="token attribute attribute-arguments operator" style="color:#393A34">=</span><span class="token attribute attribute-arguments"> </span><span class="token attribute attribute-arguments string" style="color:#e3116c">"GetTask"</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token return-type class-name">Task</span><span class="token return-type class-name punctuation" style="color:#393A34">&lt;</span><span class="token return-type class-name">IEnumerable</span><span class="token return-type class-name punctuation" style="color:#393A34">&lt;</span><span class="token return-type class-name keyword" style="color:#00009f">string</span><span class="token return-type class-name punctuation" style="color:#393A34">&gt;</span><span class="token return-type class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">GetTask</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// fire and forget</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// no await so exception will be caught in TaskScheduler.UnobservedTaskException</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// demoValues will be returned</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    demoService</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">PerformTaskAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> demoValues</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>When calling the <code>GetTask</code> endpoint, the demoValues get returned by the controller as expected.</p>
<p><img decoding="async" loading="lazy" alt="child-in-parent" src="https://your-docusaurus-test-site.com/assets/images/async-task-ca94146ff342eff6659f1625b6b4ab57.png" width="864" height="750" class="img_ev3q"></p>
<p>But in the console output we could see the log from the TaskScheduler.UnobservedTaskException handler.<br>
<!-- -->Without the UnobservedTaskException handler we would never have known about the exception in the Task.</p>
<p><img decoding="async" loading="lazy" alt="child-in-parent" src="https://your-docusaurus-test-site.com/assets/images/async-task-error-f627d76c06df9c91cd88361317e0c266.png" width="977" height="311" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="fire-and-forget-async-void---raised-in-appdomainunhandledexception-and-crashes">Fire-and-forget async void - Raised in AppDomain.UnhandledException and crashes<a href="https://your-docusaurus-test-site.com/blog/async-void#fire-and-forget-async-void---raised-in-appdomainunhandledexception-and-crashes" class="hash-link" aria-label="Direct link to Fire-and-forget async void - Raised in AppDomain.UnhandledException and crashes" title="Direct link to Fire-and-forget async void - Raised in AppDomain.UnhandledException and crashes">​</a></h3>
<p>Now to the problem we had...</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">[</span><span class="token attribute class-name">HttpGet</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">(</span><span class="token attribute attribute-arguments">Name </span><span class="token attribute attribute-arguments operator" style="color:#393A34">=</span><span class="token attribute attribute-arguments"> </span><span class="token attribute attribute-arguments string" style="color:#e3116c">"GetVoidAsync"</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token return-type class-name">Task</span><span class="token return-type class-name punctuation" style="color:#393A34">&lt;</span><span class="token return-type class-name">IEnumerable</span><span class="token return-type class-name punctuation" style="color:#393A34">&lt;</span><span class="token return-type class-name keyword" style="color:#00009f">string</span><span class="token return-type class-name punctuation" style="color:#393A34">&gt;</span><span class="token return-type class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">GetAsyncVoid</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// fire and forget</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// no await so exception will be logged in AppDomain.CurrentDomain.UnhandledException and crash</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// demoValues will be returned</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    demoService</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">PerformVoidAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> demoValues</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>When making a call to the <code>GetAsyncVoid</code> endpoint it returns the demoValues correctly which is great.</p>
<p><img decoding="async" loading="lazy" alt="child-in-parent" src="https://your-docusaurus-test-site.com/assets/images/async-void-c9b9a1e8ff958df351649b5e206b6a43.png" width="864" height="742" class="img_ev3q"></p>
<p>The <code>demoService.PerformVoidAsync</code> was an async void and as explained above, when an exception was thrown, there was nowhere for the exception to go, so it went to hell and took the service with it!</p>
<p>In the AppDomain.UnhandledException handler in my demo I was able to log the exception, but by this time it's too late and the service will inevitably crash.</p>
<p>Here you can see the output from the console window and the exception I logged, along with process exiting at the end.</p>
<p><img decoding="async" loading="lazy" alt="child-in-parent" src="https://your-docusaurus-test-site.com/assets/images/async-void-error-c7c4a56af19e569582a07cff8a94eded.png" width="976" height="512" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="key-take-aways">Key take aways<a href="https://your-docusaurus-test-site.com/blog/async-void#key-take-aways" class="hash-link" aria-label="Direct link to Key take aways" title="Direct link to Key take aways">​</a></h3>
<ul>
<li>Never use async void - use async Task.</li>
<li>If you want to fire and forget a Task, use TaskScheduler.UnobservedTaskException handler so that you can monitor unhandled exceptions in your Tasks.</li>
<li>Never use async void.</li>
<li>Never use async void.</li>
</ul>]]></content:encoded>
            <category>c#</category>
            <category>.net</category>
            <category>api</category>
            <category>async</category>
        </item>
        <item>
            <title><![CDATA[Unit Testing - Test Spy Pattern]]></title>
            <link>https://your-docusaurus-test-site.com/blog/test-spy-pattern</link>
            <guid>https://your-docusaurus-test-site.com/blog/test-spy-pattern</guid>
            <pubDate>Tue, 31 Oct 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Hi Everyone]]></description>
            <content:encoded><![CDATA[<p>Hi Everyone <!-- -->👋</p>
<p>90% of blogs I read start with something about unit testing, so to not dissapoint anyone I'm making my first blog post about unit testing <!-- -->😄</p>
<p>I'm a fan of unit testing code for various reasons, mostly because I've felt the pain when there were none! So I want to chat about a pattern I've been using for the better part of a decade but only found out today that it's called the <a href="http://xunitpatterns.com/Test%20Spy.html#:~:text=The%20Test%20Spy%20is%20designed,values%20expected%20by%20the%20test." target="_blank" rel="noopener noreferrer">Test Spy Pattern.</a></p>
<blockquote>
<p>The Test Spy is designed to act as an observation point by recording the method calls made to it by the SUT as it is exercised. During the result verification phase, the test compares the actual values passed to the Test Spy by the SUT with the values expected by the test.</p>
</blockquote>
<p>Okay I don't think my implemntation is exactly the classic Test Spy pattern, but close enough!</p>
<p>I use functions or actions to verify that method was called correctly and I can easily inspect or set the return value in each test that sets up the function or action.</p>
<p>The reason I use this pattern is because sometimes my framework of choice <a href="https://fakeiteasy.github.io/" target="_blank" rel="noopener noreferrer">FakeItEasy</a>, is not so easy <!-- -->😒<!-- -->
So I create my own implementation of the Interface or Class, which I can use across my project and makes mocking and asserting much easier.</p>
<p>So here are 2 examples where I often end up using the Test Spy Pattern. You can see my demo implementation in <a href="https://github.com/kyleoettle/test-spy-pattern" target="_blank" rel="noopener noreferrer">Github</a></p>
<ul>
<li>Mocking  HttpClient calls.</li>
<li>Verifying that a call to the ILogger has been made</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="mocking-httpclient-calls">Mocking HttpClient Calls<a href="https://your-docusaurus-test-site.com/blog/test-spy-pattern#mocking-httpclient-calls" class="hash-link" aria-label="Direct link to Mocking HttpClient Calls" title="Direct link to Mocking HttpClient Calls">​</a></h2>
<p>FakeItEasy can't directly fake an HttpClient, the required methods aren't virtual or abstract so they recommend pretty much the same approach as I'm using, by making use of the HttpMessageHandler but they have to do a bit of <a href="https://fakeiteasy.github.io/docs/7.4.0/Recipes/faking-http-client/" target="_blank" rel="noopener noreferrer">extra work</a> by calculating the call based on the return type and method name.</p>
<p>If I'm going to do extra magic, I'm going to do it in a way that's easier for me!<br>
<!-- -->Here is my implementation of spying the HttpMessageHandler used by the HttpClient and how I can verify calls being made and mock values being returned.</p>
<div class="language-cs codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cs codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">SpyHttpMessageHandler</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token type-list class-name">HttpMessageHandler</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">internal</span><span class="token plain"> </span><span class="token class-name">Func</span><span class="token class-name punctuation" style="color:#393A34">&lt;</span><span class="token class-name">HttpRequestMessage</span><span class="token class-name punctuation" style="color:#393A34">,</span><span class="token class-name"> HttpResponseMessage</span><span class="token class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> _sendAsync </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">protected</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">override</span><span class="token plain"> </span><span class="token return-type class-name">Task</span><span class="token return-type class-name punctuation" style="color:#393A34">&lt;</span><span class="token return-type class-name">HttpResponseMessage</span><span class="token return-type class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">SendAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">HttpRequestMessage</span><span class="token plain"> request</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name">CancellationToken</span><span class="token plain"> cancellationToken</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">_sendAsync </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">NotImplementedException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">nameof</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">_sendAsync</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">FromResult</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">_sendAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">request</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<p>I have a function called _sendAsync which I can use to verify the request being made and I get to mock the HttpResponseMessage. This makes it really easy to mock different response types, and models.</p>
<p>And the way we use it in our unit test:</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">[</span><span class="token attribute class-name">Fact</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">(</span><span class="token attribute attribute-arguments">DisplayName </span><span class="token attribute attribute-arguments operator" style="color:#393A34">=</span><span class="token attribute attribute-arguments"> </span><span class="token attribute attribute-arguments string" style="color:#e3116c">"When getting WorldTimeByIP and the correct value is returned"</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token return-type class-name">Task</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">GetWorldTime</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">//setup</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> response </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">WorldTime</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> datetime </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> DateTime</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Now</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> client_ip </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mockClientIp"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> responseMessage </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">HttpResponseMessage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    responseMessage</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Content </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">StringContent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">JsonConvert</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">SerializeObject</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">response</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    responseMessage</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">StatusCode </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> HttpStatusCode</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">OK</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    messageHandler</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_sendAsync </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">request</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">//assert request properties</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        Assert</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Equal</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">HttpMethod</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Get</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> request</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Method</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        Assert</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Equal</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"http://worldtimeapi.org/api/ip"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> request</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">RequestUri</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">AbsoluteUri</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">//return responseMessage</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> responseMessage</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">//act</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> worldTime </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> sut</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">GetWorldTimeFromIP</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">//assert</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Assert</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Equal</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">response</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">datetime</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> worldTime</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">datetime</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Assert</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Equal</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">response</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">client_ip</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> worldTime</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">client_ip</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>In this example my DemoClient takes in an HttpClientFactory which I use to create an instance of my HttpClient.<br>
<!-- -->The HttpClient takes in my SpyHttpMessageHandler which I use for mocking and asserting.<br>
<!-- -->Some of the benefits here are that it's really easy to assert the Request being made. I can assert the request.Method, I can assert the URL, the headers, the body, etc.<br>
<!-- -->I also get to specify the HttpResponseMessage just by serializing the model I'd like to return.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="verifying-that-a-call-to-the-ilogger-has-been-made">Verifying that a call to the ILogger has been made<a href="https://your-docusaurus-test-site.com/blog/test-spy-pattern#verifying-that-a-call-to-the-ilogger-has-been-made" class="hash-link" aria-label="Direct link to Verifying that a call to the ILogger has been made" title="Direct link to Verifying that a call to the ILogger has been made">​</a></h2>
<p>The FakeItEasy approach to faking calls to the ILogger isn't pretty and it's hard to verify that you're logging the correct data. The internet is full of examples of how you can do it and all of them is a little bit different.  The reason I went with my Test Spy Pattern is that verifying a log message should be one of the simplist things in the world, and surprisingly it wasn't. So I decided to simplify it for my unit testing.</p>
<p>Here is my implementation of spying the ILogger and how I can verify that the correct calls are being made.</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">SpyLogger</span><span class="token class-name punctuation" style="color:#393A34">&lt;</span><span class="token class-name">T</span><span class="token class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token type-list class-name">ILogger</span><span class="token type-list class-name punctuation" style="color:#393A34">&lt;</span><span class="token type-list class-name">T</span><span class="token type-list class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token return-type class-name">IDisposable</span><span class="token plain"> </span><span class="token generic-method function" style="color:#d73a49">BeginScope</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&lt;</span><span class="token generic-method generic class-name">TState</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">TState</span><span class="token plain"> state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> A</span><span class="token punctuation" style="color:#393A34">.</span><span class="token generic-method function" style="color:#d73a49">Fake</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&lt;</span><span class="token generic-method generic class-name">IDisposable</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:#00009f">bool</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">IsEnabled</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">LogLevel</span><span class="token plain"> logLevel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">NotImplementedException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">internal</span><span class="token plain"> </span><span class="token class-name">Action</span><span class="token class-name punctuation" style="color:#393A34">&lt;</span><span class="token class-name">LogLevel</span><span class="token class-name punctuation" style="color:#393A34">,</span><span class="token class-name"> EventId</span><span class="token class-name punctuation" style="color:#393A34">,</span><span class="token class-name"> </span><span class="token class-name keyword" style="color:#00009f">object</span><span class="token class-name punctuation" style="color:#393A34">,</span><span class="token class-name"> Exception</span><span class="token class-name punctuation" style="color:#393A34">?</span><span class="token class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> _logInvoked</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token generic-method function" style="color:#d73a49">Log</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&lt;</span><span class="token generic-method generic class-name">TState</span><span class="token generic-method generic class-name punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">LogLevel</span><span class="token plain"> logLevel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name">EventId</span><span class="token plain"> eventId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name">TState</span><span class="token plain"> state</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name">Exception</span><span class="token class-name punctuation" style="color:#393A34">?</span><span class="token plain"> exception</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name">Func</span><span class="token class-name punctuation" style="color:#393A34">&lt;</span><span class="token class-name">TState</span><span class="token class-name punctuation" style="color:#393A34">,</span><span class="token class-name"> Exception</span><span class="token class-name punctuation" style="color:#393A34">?</span><span class="token class-name punctuation" style="color:#393A34">,</span><span class="token class-name"> </span><span class="token class-name keyword" style="color:#00009f">string</span><span class="token class-name punctuation" style="color:#393A34">&gt;</span><span class="token plain"> formatter</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">_logInvoked </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">NotImplementedException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">nameof</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">_logInvoked</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">_logInvoked</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">logLevel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> eventId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> state</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> exception</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>I have an action called _logInvoked which I can use to verify that the logger was called correctly. It's easy to verify the LogLevel, the message or exception which are all important to me.</p>
<p>And the way we use it in our unit test:</p>
<div class="language-csharp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-csharp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">[</span><span class="token attribute class-name">Fact</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">(</span><span class="token attribute attribute-arguments">DisplayName </span><span class="token attribute attribute-arguments operator" style="color:#393A34">=</span><span class="token attribute attribute-arguments"> </span><span class="token attribute attribute-arguments string" style="color:#e3116c">"When getting WorldTimeByIP and a failed status code is returned"</span><span class="token attribute attribute-arguments punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token return-type class-name">Task</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">GetWorldTime_fail</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">//setup</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> response </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mock response"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> responseMessage </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">HttpResponseMessage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    responseMessage</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Content </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name">StringContent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">response</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    responseMessage</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">StatusCode </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> HttpStatusCode</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Conflict</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    messageHandler</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_sendAsync </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">request</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">//assert request properties</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        Assert</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Equal</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">HttpMethod</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Get</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> request</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Method</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        Assert</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Equal</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"http://worldtimeapi.org/api/ip"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> request</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">RequestUri</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">AbsoluteUri</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">//return responseMessage</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> responseMessage</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> logged </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    logger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_logInvoked </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">logLevel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> eventId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> state</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> exception</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        logged </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        Assert</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Equal</span><span class="token punctuation" style="color:#393A34">(</span><span class="token interpolation-string string" style="color:#e3116c">$"Failed to get WorldTime from IP. </span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">{</span><span class="token interpolation-string interpolation expression language-csharp">responseMessage</span><span class="token interpolation-string interpolation expression language-csharp punctuation" style="color:#393A34">.</span><span class="token interpolation-string interpolation expression language-csharp">StatusCode</span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">}</span><span class="token interpolation-string string" style="color:#e3116c">: </span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">{</span><span class="token interpolation-string interpolation expression language-csharp">response</span><span class="token interpolation-string interpolation punctuation" style="color:#393A34">}</span><span class="token interpolation-string string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> state</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">ToString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">//act</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name keyword" style="color:#00009f">var</span><span class="token plain"> worldTime </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> sut</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">GetWorldTimeFromIP</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">//assert</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Assert</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Null</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">worldTime</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Assert</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">True</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">logged</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>In this implementation I set the _logInvoked action that will be called in my SpyLogger.<br>
<!-- -->I get to look at and assert the actual values being passed to my spy logger and I find it easier to verify the values compared to other frameworks due to the simplistic nature of the Spy Test Pattern.</p>]]></content:encoded>
            <category>c#</category>
            <category>unit test</category>
        </item>
        <item>
            <title><![CDATA[Welcome to my blog!]]></title>
            <link>https://your-docusaurus-test-site.com/blog/greetings</link>
            <guid>https://your-docusaurus-test-site.com/blog/greetings</guid>
            <pubDate>Sat, 28 Oct 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Hi Everyone]]></description>
            <content:encoded><![CDATA[<p>Hi Everyone <!-- -->👋</p>
<p>Welcome to my blog! I'm Kyle, a Software Engineer who loves writing code, sharing what I learn and reading blogs. I often have a million tabs open from reading a blog and deep diving in to the author's content. It's part of what I love about software engineers and our sharing culture.</p>
<p>Thanks for stopping by! Hope you find something useful here <!-- -->🚀</p>]]></content:encoded>
            <category>greetings</category>
            <category>introduction</category>
        </item>
    </channel>
</rss>