<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Russ Hammett</title>
    <description>The latest articles on Forem by Russ Hammett (@kritner).</description>
    <link>https://forem.com/kritner</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F89783%2Fe2f73b74-c32a-4c13-a941-9a59ac689b6a.jpeg</url>
      <title>Forem: Russ Hammett</title>
      <link>https://forem.com/kritner</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kritner"/>
    <language>en</language>
    <item>
      <title>Microsoft Orleans: Grain Caches</title>
      <dc:creator>Russ Hammett</dc:creator>
      <pubDate>Thu, 15 Dec 2022 16:00:00 +0000</pubDate>
      <link>https://forem.com/kritner/microsoft-orleans-grain-caches-471l</link>
      <guid>https://forem.com/kritner/microsoft-orleans-grain-caches-471l</guid>
      <description>&lt;p&gt;Holy moly it’s been a while. Time for some more Orleans goodness, hopefully? Caches!&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;What are caches and what do they do? As usual, going to lean on Wikipedia to help me out:&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://en.wikipedia.org/wiki/Cache_(computing)"&gt;https://en.wikipedia.org/wiki/Cache_(computing)&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In computing, a cache is a hardware or software component that stores data so that future requests for that data can be served faster; the data stored in a cache might be the result of an earlier computation or a copy of data stored elsewhere.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So basically a means of getting data faster, sign me up!&lt;/p&gt;

&lt;p&gt;Often, you’ll see multiple levels of caching on varying pieces of infrastructure, whether those pieces are all contained within a single piece of hardware (like a computer), or spread across multiple pieces of hardware (like a network). We have the need for speed, and caches gonna give it to ya… ideally!&lt;/p&gt;

&lt;h2&gt;
  
  
  Problems with caching
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/builders-library/caching-challenges-and-strategies/"&gt;https://aws.amazon.com/builders-library/caching-challenges-and-strategies/&lt;/a&gt; has a lot of good information, but just to name a few:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stale data&lt;/li&gt;
&lt;li&gt;Inconsistent data across caches&lt;/li&gt;
&lt;li&gt;In the case of local caches - memory and/or additional CPU pressure on a system that is otherwise acting as a webserver&lt;/li&gt;
&lt;li&gt;In the case of external caches - additional system complexity, additional hop to get to data (though seemingly still better than having to rerun some sort of querying/aggregating of data from potentially numerous data sources when not caching)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A (naive) Orleans Cache
&lt;/h2&gt;

&lt;p&gt;So, it’s been a while since I’ve written &lt;em&gt;anything&lt;/em&gt;, much less about Orleans. It’s Hacktoberfest though so I at least gotta try to get that shirt!&lt;/p&gt;

&lt;p&gt;Like in past posts where I’m demonstrating something Orleansly, we’ll be working from my github repo starting here &lt;a href="https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.62.0"&gt;https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.62.0&lt;/a&gt;. Prior to us creating another &lt;code&gt;IOrleansFunction&lt;/code&gt;, we actually need to write the thing we’re be demonstrating, the cache!&lt;/p&gt;

&lt;p&gt;A cache abstraction can (and perhaps should?) have more methods available to it than what we’ll be doing in this post, but this isn’t meant to be a full fledged solution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IOrleansCache&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TValue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IGrainWithStringKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IGrainInterfaceMarker&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;AddOrUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TValue&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TValue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above should look pretty straight forward, but just to go over it anyway:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TValue&lt;/code&gt; is the type of value our cache will be storing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IGrainWithStringKey&lt;/code&gt; states that cache will have a string primary key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AddOrUpdate&lt;/code&gt; - for adding/updating &lt;code&gt;value&lt;/code&gt;s based on a string &lt;code&gt;key&lt;/code&gt; (not to be confused with the grain primary key)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Get&lt;/code&gt; for retrieving a &lt;code&gt;value&lt;/code&gt; from the cache based on a string &lt;code&gt;key&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In case it isn’t obvious, we have two “keys” in the above declaration. A grain “primary” key, and a “key value pair” key. What’s the difference? Well by having the primary key be a string, we can get different grain instances for each individual string key we provide. Why would we do that? You may have different caches with different value types you want to store, bring up a different cache per customer, or a bit of both.&lt;/p&gt;

&lt;p&gt;As for the implementation of the grain, I’m leaning heavily on an LRU cache from the package &lt;a href="https://github.com/bitfaster/BitFaster.Caching/"&gt;https://github.com/bitfaster/BitFaster.Caching/&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrleansLruCache&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TValue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Grain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IOrleansCache&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TValue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ConcurrentLru&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TValue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;OrleansLruCache&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_cache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ConcurrentLru&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TValue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;AddOrUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TValue&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOrUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TValue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;TValue&lt;/span&gt; &lt;span class="n"&gt;nullResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again this should all be pretty self explanatory, just a plain old consuming of a cache and not (currently) worrying about things like parameterizing configuration, providing cache eviction methods, persisting the cache to disk in any way, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;Keeping this one pretty short and sweet, I implemented within the final code (found here &lt;a href="https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.63.0"&gt;https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.63.0&lt;/a&gt;) our &lt;code&gt;IOrleansFunction&lt;/code&gt; so it can be played around with. I might have another post or two this month elaborating on this some more, just cuz I need to do some more PRs! (And get back into the habit of writing, but this is at least the 50th time I’ve said that)&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Cache_(computing)"&gt;https://en.wikipedia.org/wiki/Cache_(computing)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/builders-library/caching-challenges-and-strategies/"&gt;https://aws.amazon.com/builders-library/caching-challenges-and-strategies/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bitfaster/BitFaster.Caching/"&gt;https://github.com/bitfaster/BitFaster.Caching/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Code (beginning) &lt;a href="https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.62.0"&gt;https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.62.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Code (end) &lt;a href="https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.63.0"&gt;https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.63.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Photo by &lt;a href="https://unsplash.com/photos/mkxTOAxqTTo"&gt;“ashin_k_suresh”&lt;/a&gt; on &lt;a href="https://unsplash.com/"&gt;Unsplash&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>csharp</category>
      <category>netcore</category>
      <category>microsoftorleans</category>
    </item>
    <item>
      <title>Microsoft Orleans - Orleans.SyncWork now a part of OrleansContrib</title>
      <dc:creator>Russ Hammett</dc:creator>
      <pubDate>Wed, 01 Dec 2021 18:40:00 +0000</pubDate>
      <link>https://forem.com/kritner/microsoft-orleans-orleanssyncwork-now-a-part-of-orleanscontrib-i2p</link>
      <guid>https://forem.com/kritner/microsoft-orleans-orleanssyncwork-now-a-part-of-orleanscontrib-i2p</guid>
      <description>&lt;p&gt;The package I wrote about in &lt;a href="https://dev.to/2021/11/22/microsoft-orleans-long-running-cpu-bound-work/"&gt;this post&lt;/a&gt; is now a part of the &lt;a href="https://github.com/OrleansContrib"&gt;OrleansContrib&lt;/a&gt; organization!&lt;/p&gt;

&lt;p&gt;This is a pretty short post, I think, but after a &lt;a href="https://twitter.com/RLHammett/status/1461814378710409217"&gt;tweet&lt;/a&gt; about the release of &lt;a href="https://github.com/OrleansContrib/Orleans.SyncWork"&gt;Orleans.SyncWork&lt;/a&gt;, there was a bit of back and forth in the thread:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iSgFbeKI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2021/12/01/microsoft-orleans-orleans-syncwork-now-a-part-of-orleanscontrib/tweet.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iSgFbeKI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2021/12/01/microsoft-orleans-orleans-syncwork-now-a-part-of-orleanscontrib/tweet.PNG" alt="back and forth in a twitter thread" width="496" height="832"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I reached out to Reuben shortly after, and now the repository is a part of the OrleansContrib organization! I don’t think this actually means anything of super significance, but it’s pretty cool! Maybe the package will now get better visibility!&lt;/p&gt;

&lt;p&gt;That’s it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/2021/11/22/microsoft-orleans-long-running-cpu-bound-work/"&gt;Microsoft Orleans - Long running CPU bound work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/OrleansContrib/Orleans.SyncWork"&gt;OrleansContrib/Orleans.SyncWork&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>c</category>
      <category>netcore</category>
      <category>microsoftorleans</category>
    </item>
    <item>
      <title>CI/CD for .net 6, using GitHub actions</title>
      <dc:creator>Russ Hammett</dc:creator>
      <pubDate>Wed, 01 Dec 2021 01:50:51 +0000</pubDate>
      <link>https://forem.com/kritner/cicd-for-net-6-using-github-actions-1kln</link>
      <guid>https://forem.com/kritner/cicd-for-net-6-using-github-actions-1kln</guid>
      <description>&lt;p&gt;With the publishing of &lt;a href="https://github.com/OrleansContrib/Orleans.SyncWork/" rel="noopener noreferrer"&gt;Orleans.SyncWork&lt;/a&gt;, I’ve had the opportunity to explore GitHub actions - which is a way to automate workflows. Here’s some of my first experience into the “action” (groan).&lt;/p&gt;

&lt;h2&gt;
  
  
  Automated workflow
&lt;/h2&gt;

&lt;p&gt;First things first, what even is a workflow, and what does it mean to automate one? Well dear potential reader, a workflow is nothing more than a set of steps taken to complete a task.&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://en.wikipedia.org/wiki/Workflow" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A workflow consists of an orchestrated and repeatable pattern of activity, enabled by the systematic organization of resources into processes that transform materials, provide services, or process information. It can be depicted as a sequence of operations, the work of a person or group, the work of an organization of staff, or one or more simple or complex mechanisms.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can think of a workflow as the steps taken to accomplish “something”. That “something” can be any number of things, related to any number of subjects. In the context of this post, we’ll be mostly covering workflows as it relates to a build and release pipeline, also commonly referred to as continuous integration (CI) and continuous delivery (CD).&lt;/p&gt;

&lt;p&gt;I’d like to cover both the CI and CD aspects of the &lt;a href="https://github.com/OrleansContrib/Orleans.SyncWork/" rel="noopener noreferrer"&gt;Orleans.SyncWork&lt;/a&gt;, so let’s get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you’ll (probably) need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Experience working with a &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/tools/" rel="noopener noreferrer"&gt;CLI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.kritner.com/2018/12/06/getting-started-with-xunit/" rel="noopener noreferrer"&gt;Unit tests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;An idea of the steps that you need to take in order to build, test, and deploy your code. If these steps are already in your head in the form of CLI commands, then you’re already most of the way there!&lt;/li&gt;
&lt;li&gt;Some patience in getting your workflow properly laid out&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Continuous Integration
&lt;/h2&gt;

&lt;p&gt;Before you’re able to deploy code through a work flow (continuous delivery), you need to be able to integrate it safely into your main/trunk. For dotnet, through a handful of CLI commands, the building and testing of code is pretty straight forward. Doing CI has the added benefit of bringing up a brand new environment for builds, each and every time, a similar idea to why I’ve been a proponent of &lt;a href="https://blog.kritner.com/2015/01/21/And-its-like-whats-the-deal-with-build-servers" rel="noopener noreferrer"&gt;build servers&lt;/a&gt; for so long.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The above command is the minimum you need to build either a solution file or project file on the dotnet side of things. From a continuous integration perspective, you may want to throw a few flags onto the command, such as:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet build --configuration release
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and things of that nature, see what’s available to you with the &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-build" rel="noopener noreferrer"&gt;dotnet build&lt;/a&gt; documentation.&lt;/p&gt;

&lt;p&gt;That could look like this from the CLI:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2021%2F11%2F29%2Fcicd-for-net6-with-github-actions%2FdotnetBuild.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2021%2F11%2F29%2Fcicd-for-net6-with-github-actions%2FdotnetBuild.PNG" alt="dotnet build"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Test
&lt;/h3&gt;

&lt;p&gt;Next is testing. I’ve probably already said it too many times, but &lt;a href="https://blog.kritner.com/2018/12/06/getting-started-with-xunit/" rel="noopener noreferrer"&gt;test your code&lt;/a&gt;! Especially if you’re building libraries! Tests help ensure that the code you’re writing does what you say it does. Additionally tests be used as “documentation” in a way, if the tests are named well, and are invoking the code in a similar manner to how your consumer will use it, they’ll be in a better place to get started using what you’ve delivered.&lt;/p&gt;

&lt;p&gt;Like the build command, the test command is quite straightforward as well:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Of course the above is the absolute bare minimum command, there are lots of parameters that can be passed to &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test" rel="noopener noreferrer"&gt;dotnet test&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;A test run could look like this from the CLI:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2021%2F11%2F29%2Fcicd-for-net6-with-github-actions%2FdotnetTest.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2021%2F11%2F29%2Fcicd-for-net6-with-github-actions%2FdotnetTest.PNG" alt="dotnet test"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  CI Action
&lt;/h3&gt;

&lt;p&gt;With the above &lt;code&gt;dotnet build&lt;/code&gt; and &lt;code&gt;dotnet test&lt;/code&gt; commands, we have &lt;em&gt;most&lt;/em&gt; of what we’ll need to put together an action to build and test our code, automatically!&lt;/p&gt;

&lt;p&gt;There is lots of good information, even some specific to .net testing on the &lt;a href="https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. I pretty much used the documentation as a starting point, and ended up with this…&lt;/p&gt;

&lt;p&gt;.github/workflows/ci.yml:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and test&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup .NET&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-dotnet@v1&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;dotnet-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;6.0.x&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore dependencies&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet restore&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet build -c Release --no-restore&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet test -c Release --no-build --verbosity normal --filter "Category!=LongRunning"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The above file should be mostly straightforward, first we give a name to our workflow with &lt;code&gt;name&lt;/code&gt;, specify the triggers for the workflow, in this case “on pull requests against the main branch”. The file then goes on to define the job “build”, which specifies an OS to run on, then steps. The steps do a few things of note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checkout the code, placing it into the ubuntu instance that is being utilized from the step previous&lt;/li&gt;
&lt;li&gt;Setup .NET with a prebuilt action&lt;/li&gt;
&lt;li&gt;restore dependencies

&lt;ul&gt;
&lt;li&gt;This is an explicit step, rather than implicit from our previous &lt;code&gt;dotnet build&lt;/code&gt; command, as if this &lt;em&gt;explicit&lt;/em&gt; restore step were to fail, we’d more quickly know at what point of the build there is a failure&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;build the solution file at the project root&lt;/li&gt;
&lt;li&gt;finally test the code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have a few new flags in our build and test commands, namely specifying a configuration of release, and don’t restore/build on steps after those steps having already occurred. One final note is the &lt;code&gt;--filter "Category!=LongRunning"&lt;/code&gt; - I was having trouble with the test runner getting through the tests I had laid out. They took 3 minutes to run locally, but ran for over 25 minutes on the build agent. Due to this fact, I added some classifications of “category” to the longer running tests, and excluded them from the test run in the above ci.yml file.&lt;/p&gt;
&lt;h2&gt;
  
  
  Continuous Delivery
&lt;/h2&gt;

&lt;p&gt;Continuous delivery is a lot like continuous integration, and builds on top of it. I’m of the thinking that CD should do everything CI should do, or perhaps even better, actually rely on the CI, rather than redefining the steps in your CICD like I ended up doing. That was a bit rambly, but CD should do everything CI should do, except with the additional step of actually &lt;em&gt;delivering&lt;/em&gt; (deploying/pushing) the code as a part of its workflow.&lt;/p&gt;
&lt;h3&gt;
  
  
  Delivery Complexities
&lt;/h3&gt;

&lt;p&gt;That delivery part can have a lot of nuance to it that ups the complexity by a significant amount when compared to just “CI”. What does it mean to actually deliver code? Well, that could depend a lot on what type of code you’re actually delivering. In my case, I’m delivering a NuGet package, which has its own complexities, but what else is there? Well the other obvious thing that comes to mind is a web site / web api, one which could potentially have database changes to roll out in addition to the code. This to me, has the potential to be worlds more complex than just pushing a NuGet package up. How do you not only handle failures, but detect them and roll back, in the case of something going wrong with either you web push or database push? Perhaps I’ll be able to explore that one day, but for now, let’s get back to the NuGet package.&lt;/p&gt;

&lt;p&gt;So, is there a complexity with delivering a NuGet package? Yes. NuGet package versioning can be a big undertaking when it comes to manual deployments, much less CD; as there is a requirement of NuGet packages being immutable. Does this mean that for &lt;em&gt;every&lt;/em&gt; check in, on &lt;em&gt;every&lt;/em&gt; potential branch that will be pushed to NuGet, you need to update some text file or code to indicate the next built version? That was my initial thinking, but thankfully that is not the case with the help of &lt;a href="https://github.com/dotnet/Nerdbank.GitVersioning/" rel="noopener noreferrer"&gt;Nerdbank.GitVersioning&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I don’t think I have my CICD set up &lt;em&gt;exactly&lt;/em&gt; how I’ll end up having it, but for right now it works. I installed the NerdBank.GitVersioning tool and package, and now for each build, I get a unique version number for the NuGet package upon build. I can toggle between prerelease or release packages, and can even publish “nightly” builds that contain a commit hash on them, all in the name of uniquely identifiable NuGet packages.&lt;/p&gt;

&lt;p&gt;There was a fair amount of setup, that in some ways I’m still working through, but this article is already getting long enough, take a look at the PR(s) if you’re curious: &lt;a href="https://github.com/OrleansContrib/Orleans.SyncWork/pull/8" rel="noopener noreferrer"&gt;https://github.com/OrleansContrib/Orleans.SyncWork/pull/8&lt;/a&gt; and &lt;a href="https://github.com/OrleansContrib/Orleans.SyncWork/pull/13" rel="noopener noreferrer"&gt;https://github.com/OrleansContrib/Orleans.SyncWork/pull/13&lt;/a&gt;. The “tldr” of it is, &lt;code&gt;nbgv&lt;/code&gt; tooling uses the git history to rev the version number being used during builds, allowing for unique build numbers each time the CI/CD fires.&lt;/p&gt;
&lt;h3&gt;
  
  
  CD Action
&lt;/h3&gt;

&lt;p&gt;There’s been a fair amount of information so far, but between our CI action and the information about GitVersioning, we have everything we need to put together a “first pass” cicd.yml. For CI, we were doing builds/tests against PRs to main. For CD, we’ll want to do delivery when code is &lt;em&gt;pushed&lt;/em&gt; to main, as well as branches that begin with “RELEASE/v*”. My thinking here is that since we’ll be integrating often into main (theoretically), we don’t necessarily want to create full “new release packages” for &lt;em&gt;every&lt;/em&gt; commit to main. We could however, create “prerelease” NuGet packages to main for each commit, making those changes available to the NuGet feed, but them not being labeled as a release version. Otherwise I have Nerdbank.GitVersioning set up to release “release” versions of packages from the “RELEASE/v*” branches.&lt;/p&gt;

&lt;p&gt;The CD action file itself will look &lt;em&gt;very&lt;/em&gt; similar to the CI one, just with the few additions of &lt;code&gt;dotnet nuget...&lt;/code&gt; commands, shown below:&lt;/p&gt;

&lt;p&gt;.github/workflows/cicd.yml&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build, test, and deploy&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;main'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RELEASE/v**'&lt;/span&gt;


&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup .NET&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-dotnet@v1&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;dotnet-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;6.0.x&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore dependencies&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet restore&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet build -c Release --no-restore&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet test -c Release --no-restore --no-build --verbosity normal --filter "Category!=LongRunning"&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pack&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet pack src/Orleans.SyncWork/Orleans.SyncWork.csproj -c Release --no-restore --no-build --include-symbols -p:SymbolPackageFormat=snupkg -o .&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Push to NuGet&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet nuget push *.nupkg --skip-duplicate -s https://api.nuget.org/v3/index.json -k ${{secrets.NUGET_API_KEY}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the above, you’ll notice that it’s more than 50% “the same file” as CI. I was going to potentially look into &lt;a href="https://docs.github.com/en/actions/creating-actions/creating-a-composite-action" rel="noopener noreferrer"&gt;composite actions&lt;/a&gt; as some point, so see if I could instead “chain” CI and CD, rather than redefining the CI within the CD file; but I’ve not yet had a chance to explore that.&lt;/p&gt;

&lt;p&gt;Aside from the change to the “on” event (pull_request -&amp;gt; push), there are two new commands at the bottom &lt;code&gt;dotnet pack&lt;/code&gt; and &lt;code&gt;dotnet nuget push&lt;/code&gt;. The &lt;code&gt;dotnet pack&lt;/code&gt; command is used to “package” up the project specified into a “.nupkg” file (and snupkg in this case for symbols). Finally the &lt;code&gt;dotnet nuget push&lt;/code&gt; command is used to push those newly packed NuGet package(s) to the feed specified of nuget.org. On this command you’ll also see the &lt;code&gt;{{secrets.NUGET_API_KEY}}&lt;/code&gt; portion of the command, this is defined as a &lt;a href="https://docs.github.com/en/actions/security-guides/encrypted-secrets" rel="noopener noreferrer"&gt;repository secret&lt;/a&gt; and it can be used to pass “secret” information to things like workflows, in this case it’s my NuGet API key. These secrets can be set from the repository “Settings” -&amp;gt; “Secrets”:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2021%2F11%2F29%2Fcicd-for-net6-with-github-actions%2Fsecrets.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2021%2F11%2F29%2Fcicd-for-net6-with-github-actions%2Fsecrets.PNG" alt="Repository secrets"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Still to do
&lt;/h2&gt;

&lt;p&gt;The “putting out a release branch” is still a bit of a manual step for me. I need to run &lt;code&gt;nbgv prepare-release&lt;/code&gt; from my &lt;em&gt;local&lt;/em&gt; environment, then push up the subsequently created “RELEASE/v*” branch and updated new pre-release version that is created under main.&lt;/p&gt;

&lt;p&gt;That may not have made sense.&lt;/p&gt;

&lt;p&gt;If I’m working in main with a prerelease version of “1.0-prerelease”, when I &lt;code&gt;nbgv prepare-release&lt;/code&gt;, main will be (as an example) updated to “1.1-prerelease” with a branch called “RELEASE/v1.0” created having a release version of “1.0”. The push of these two changes will &lt;em&gt;currently&lt;/em&gt; build a new prerelease package of “1.1-prerelease” and release package of “1.0”, both of which will contain “the same content” at the time of being pushed.&lt;/p&gt;

&lt;p&gt;I’m not sure how I feel about the above. I like the automatic build and deploying of packages, but I don’t like having to create the release locally. I could conceivably create a manually dispatched workflow that did this release preparation for me, but then there’d still be the slight strangeness around immediately pushing out a prerelease package with no changes in comparison to the “previous” pre-release package built, and the new release package being built. I’m not sure what the “right flow” is quite yet, what I have right now &lt;em&gt;does work&lt;/em&gt;, it just seems a &lt;em&gt;bit&lt;/em&gt; messy.&lt;/p&gt;

&lt;p&gt;Perhaps I’ll eventually look into workflows more like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CI - continues to be run on PRs to main&lt;/li&gt;
&lt;li&gt;CICD - can be executed against the main branch and “RELEASE/v*” branches, but is not done automatically as it currently is

&lt;ul&gt;
&lt;li&gt;I’m not sure about this part, main and the release should theoretically always be deployable, but do I really want to deploy &lt;em&gt;every&lt;/em&gt; time there’s a change to main…?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Prepare release workflow - with this workflow, I’d want to do the “local” steps I currently take for preparing a release, but do it through a github action.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  My Workflow
&lt;/h3&gt;

&lt;p&gt;I wasn't sure if the literal sections were required, so here they are, "My Workflow" I think was pretty well described above, hopefully!&lt;/p&gt;
&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Maintainer Must-Haves&lt;/li&gt;
&lt;li&gt;DIY Deployments&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Yaml File or Link to Code
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/OrleansContrib" rel="noopener noreferrer"&gt;
        OrleansContrib
      &lt;/a&gt; / &lt;a href="https://github.com/OrleansContrib/Orleans.SyncWork" rel="noopener noreferrer"&gt;
        Orleans.SyncWork
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      This package's intention is to expose an abstract base class to allow https://github.com/dotnet/orleans/ to work with long running CPU bound synchronous work, without becoming overloaded.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a href="https://github.com/OrleansContrib/Orleans.SyncWork/actions/workflows/ci.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/OrleansContrib/Orleans.SyncWork/actions/workflows/ci.yml/badge.svg?branch=main" alt="Build and test"&gt;&lt;/a&gt; &lt;a href="https://coveralls.io/github/OrleansContrib/Orleans.SyncWork?branch=main" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/000d7054c3006cbe524a074a0c5d204caf82c0831556a60403e9fd380797ca8c/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f4f726c65616e73436f6e747269622f4f726c65616e732e53796e63576f726b2f62616467652e7376673f6272616e63683d6d61696e" alt="Coverage Status"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/1a392178cea32fdcf16944bf8ebb82ef7147faf073a76fce229fba8ad320adfd/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f4f726c65616e732e53796e63576f726b"&gt;&lt;img src="https://camo.githubusercontent.com/1a392178cea32fdcf16944bf8ebb82ef7147faf073a76fce229fba8ad320adfd/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f4f726c65616e732e53796e63576f726b" alt="Latest NuGet Version"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/d9b34f24a446b6593bdb31e4f8ef409e1775a27d6b34fcb351e4bb19bcdd69d1/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4f726c65616e73436f6e747269622f4f726c65616e732e53796e63576f726b"&gt;&lt;img src="https://camo.githubusercontent.com/d9b34f24a446b6593bdb31e4f8ef409e1775a27d6b34fcb351e4bb19bcdd69d1/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4f726c65616e73436f6e747269622f4f726c65616e732e53796e63576f726b" alt="License"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This package's intention is to expose an abstract base class to allow &lt;a href="https://github.com/dotnet/orleans/" rel="noopener noreferrer"&gt;Orleans&lt;/a&gt; to work with long running, CPU bound, synchronous work, without becoming overloaded.&lt;/p&gt;
&lt;p&gt;Built with an open source &lt;a href="https://jb.gg/OpenSourceSupport" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FOrleansContrib%2FOrleans.SyncWorkdocs%2Fimages%2FRider_icon.svg" width="25" height="25"&gt;&lt;/a&gt; license, thanks Jetbrains!&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Building&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;The project was built primarily with .net3 in mind, though the varying major version releases support .net6, .net7, and .net8; depending on the package version (should mirror the .net versions).&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Requirements&lt;/h3&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/6.0" rel="nofollow noopener noreferrer"&gt;.net 6.0 SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/7.0" rel="nofollow noopener noreferrer"&gt;.net 7.0 SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/8.0" rel="nofollow noopener noreferrer"&gt;.net 8.0 SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dotnet/format" rel="noopener noreferrer"&gt;dotnet-format&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Project Overview&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;There are several projects within this repository, all with the idea of demonstrating and/or testing the claim that the NuGet package &lt;a href="https://www.nuget.org/packages/Orleans.SyncWork/" rel="nofollow noopener noreferrer"&gt;https://www.nuget.org/packages/Orleans.SyncWork/&lt;/a&gt; does what it is claimed it does.&lt;/p&gt;

&lt;p&gt;Note that this project's major revision is kept in-line with the Orleans major version, so the project
does not necessarily abide by &lt;a href="https://semver.org/" rel="nofollow noopener noreferrer"&gt;SemVer&lt;/a&gt;, but we try as much as possible to do so. If breaking changes are introduced, descriptions of the breaking change…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/OrleansContrib/Orleans.SyncWork" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/OrleansContrib/Orleans.SyncWork" rel="noopener noreferrer"&gt;Orleans.SyncWork on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Workflow" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/core/tools/" rel="noopener noreferrer"&gt;CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.kritner.com/2015/01/21/And-its-like-whats-the-deal-with-build-servers" rel="noopener noreferrer"&gt;And it’s like what’s the deal with build servers?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.kritner.com/2018/12/06/getting-started-with-xunit/" rel="noopener noreferrer"&gt;Getting started with XUnit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-build" rel="noopener noreferrer"&gt;dotnet build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test" rel="noopener noreferrer"&gt;dotnet test&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net" rel="noopener noreferrer"&gt;Building and Testing with .NET using GitHub actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dotnet/Nerdbank.GitVersioning/" rel="noopener noreferrer"&gt;Nerdbank.GitVersioning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions/creating-actions/creating-a-composite-action" rel="noopener noreferrer"&gt;GitHub Composite Actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions/security-guides/encrypted-secrets" rel="noopener noreferrer"&gt;GitHub Encrypted Secrets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>github</category>
      <category>actionshackathon21</category>
    </item>
    <item>
      <title>Microsoft Orleans - Long running CPU bound work</title>
      <dc:creator>Russ Hammett</dc:creator>
      <pubDate>Wed, 01 Dec 2021 01:47:21 +0000</pubDate>
      <link>https://forem.com/kritner/microsoft-orleans-long-running-cpu-bound-work-374e</link>
      <guid>https://forem.com/kritner/microsoft-orleans-long-running-cpu-bound-work-374e</guid>
      <description>&lt;p&gt;I’ve been talking about the idea of long running, CPU bound, synchronous work for a long time. Now, I’ve finally taken the time to build it out into a NuGet package!&lt;/p&gt;

&lt;p&gt;Been talking about it for years at this point. Between my &lt;a href="https://blog.kritner.com/tags/microsoft-orleans/" rel="noopener noreferrer"&gt;other posts&lt;/a&gt; on Orleans, or that one time (so far?) I was on a &lt;a href="https://dotnetcore.show/episode-21-orleans-with-russell-hammett/" rel="noopener noreferrer"&gt;podcast&lt;/a&gt; to talk about it, I never got around to writing the thing down that I was always talking about. That has changed now in the form, and you too can solve problems (maybe!) with a small amount of code exposed by this NuGet package!&lt;/p&gt;

&lt;p&gt;As a refresher, I needed to come up with a means of distributing compute calls for 10s of thousands of crypto calls, all hitting within a single moment. I had done some experimenting/proof of concepting, and at the time settled on Orleans to accomplish the distribution. Though I (still) haven’t used Orleans in more of an “intended use-case” of doing lots of distributed asynchronous work, using it in the way we are works &lt;em&gt;pretty well&lt;/em&gt; for our use case.&lt;/p&gt;

&lt;p&gt;If I were to do it all over again, perhaps it would make more sense to utilize a message queueing system with tried and true infrastructure. I’m not sure if I didn’t &lt;em&gt;know&lt;/em&gt; of these concepts at the time, but this route may have been a better option. Even this option seems like it would “conceptually” work, but I’ve still not personally work bringing up a queue system with workers and notifications.&lt;/p&gt;

&lt;p&gt;Anyway, onto the NuGet package!&lt;/p&gt;

&lt;h2&gt;
  
  
  The package
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The package repository can be found: &lt;a href="https://github.com/OrleansContrib/Orleans.SyncWork" rel="noopener noreferrer"&gt;https://github.com/OrleansContrib/Orleans.SyncWork&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;And on NuGet: &lt;a href="https://www.nuget.org/packages/Orleans.SyncWork/" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/Orleans.SyncWork/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are only a few moving parts when it comes to the package:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ISyncWorker&lt;/li&gt;
&lt;li&gt;SyncWorker&lt;/li&gt;
&lt;li&gt;LimitedConcurrencyLevelTaskScheduler&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ISyncWorker
&lt;/h3&gt;

&lt;p&gt;All your CPU bound, long running, and synchronous work will be implementing from this class, though it will be through the extension of the &lt;code&gt;SyncWorker&amp;lt;TRequest, TResult&amp;gt;&lt;/code&gt; abstract base class.&lt;/p&gt;

&lt;p&gt;This interface exposes a few separate methods/contracts that allow for the interaction with the long running grain work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Start(TRequest)&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;This method is the “Entry point” into dispatching work to the grain. This method is the only one that takes in a parameter in the form of the arguments needed to perform the work. In the case of &lt;a href="https://github.com/OrleansContrib/Orleans.SyncWork/blob/main/samples/Orleans.SyncWork.Demo.Api/Services/Grains/PasswordVerifier.cs" rel="noopener noreferrer"&gt;PasswordVerifier&lt;/a&gt;, those parameters are both “the password” and “the password hash”, both of which are needed to actually do the verification of a password.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;GetWorkStatus()&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;This method is used as a sort of “polling” mechanism, where callers can retrieve the current status of the grain work being worked on. The statuses &lt;code&gt;Completed&lt;/code&gt; or &lt;code&gt;Faulted&lt;/code&gt; will then allow for the return of data from one of the following two methods, depending no which status the grain is in.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;GetResult()&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;This method gets the result of a grain’s execution when the grain is in a &lt;code&gt;Completed&lt;/code&gt; state, in the form of &lt;code&gt;TResult&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;GetException()&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;This method gets the exception from a grain’s execution when the grain is in a &lt;code&gt;Faulted&lt;/code&gt; state.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  SyncWorker
&lt;/h3&gt;

&lt;p&gt;This is the abstract base class of the grains. This class implements &lt;code&gt;ISyncWorker&amp;lt;TRequest, TResult&amp;gt;&lt;/code&gt; and provides implementation details for the methods describe from the previously mentioned interface, as well as a few other methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CreateTask(TRequest request)&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;This private method is invoked by the &lt;code&gt;Start(TRequest request)&lt;/code&gt; method, which sets the &lt;code&gt;_task&lt;/code&gt; state on the instance, and enqueues that long running work onto a &lt;code&gt;LimitedConcurrencyLevelTaskScheduler&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;PerformWork(TRequest request)&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;This is the abstract method to this abstract class, it is invoked through the creation of the &lt;code&gt;_task&lt;/code&gt;, and is the “thing” that needs to be implemented within the implementations of this class.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  LimitedConcurrencyLevelTaskScheduler
&lt;/h3&gt;

&lt;p&gt;This is a class that was more or less copied from &lt;a href="https://msdn.microsoft.com/en-us/library/ee789351(v=vs.100).aspx" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Its intended use is to limit the amount of work that can be performed at any one time as it relates to the work queued against this scheduler.&lt;/p&gt;

&lt;p&gt;Without making use of this scheduler, queueing a massive amount of work on the “normal” scheduler quickly overwhelms the Orleans silo in such a way that there are no resources available to accommodate the asynchronous messaging calls required for Orleans to operate. Using this scheduler, at some configurable level under the “amount of work that conceivably be done concurrently”, the Orleans cluster is able to continue asynchronous communication, while also performing these long running tasks being worked through the limited concurrency task scheduler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not the package
&lt;/h2&gt;

&lt;p&gt;Aside from the package itself, the &lt;a href="https://github.com/OrleansContrib/Orleans.SyncWork" rel="noopener noreferrer"&gt;repository&lt;/a&gt; has a few other projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Orleans.SyncWork.Demo.Api&lt;/li&gt;
&lt;li&gt;Orleans.SyncWork.Demo.Api.Benchmark&lt;/li&gt;
&lt;li&gt;Orleans.SyncWork.Tests&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Orleans.SyncWork.Demo.Api
&lt;/h3&gt;

&lt;p&gt;a “sample” implementation of a set of Orleans grains, as well as the cluster hosting. In this project, several long running grains are implemented and registered to the Orleans silo. Those grains are then exposed through api endpoints, or are used as a means of testing some of the logic of &lt;code&gt;SyncWorker&amp;lt;TRequest, TResult&amp;gt;&lt;/code&gt; within the Orleans.SyncWork.Tests project.&lt;/p&gt;

&lt;p&gt;This project exposes an &lt;a href="https://dev.to/2018/10/31/Microsoft-Orleans-Reporting-Dashboard/"&gt;Orleans Dashboard&lt;/a&gt;, as well as SwaggerUI, for keeping an eye on the test cluster and invoking calls to the API respectively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2021%2F11%2F22%2Fmicrosoft-orleans-long-running-cpu-bound-work%2Fdashboard.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2021%2F11%2F22%2Fmicrosoft-orleans-long-running-cpu-bound-work%2Fdashboard.PNG" alt="The dashboard depicting 10k grains being fired off simultaneously"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Orleans.SyncWork.Demo.Api.Benchmark
&lt;/h3&gt;

&lt;p&gt;A &lt;a href="https://benchmarkdotnet.org/index.html" rel="noopener noreferrer"&gt;Benchmark&lt;/a&gt; project has been set up to get an idea of the timing differences between serial execution, &lt;code&gt;Parallel.For&lt;/code&gt;, and the parallel execution offered through the &lt;code&gt;SyncWork&amp;lt;TRequest, TResponse&amp;gt;&lt;/code&gt; implementation. The latter can be slower then &lt;code&gt;Parallel.For&lt;/code&gt;, but faster then the serial execution - though keep in mind this benchmarking testing is done completely locally, where in a real world scenario, you’d be bringing up multiple silo hosts to make up a highly available cluster. At this point it stands to reason that the functionality exposed by this package will far exceed the performance accomplished with a single machine.&lt;/p&gt;

&lt;p&gt;An initial running of the benchmark gave the results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Mean&lt;/th&gt;
&lt;th&gt;Error&lt;/th&gt;
&lt;th&gt;StdDev&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Serial&lt;/td&gt;
&lt;td&gt;12.284 s&lt;/td&gt;
&lt;td&gt;0.0145 s&lt;/td&gt;
&lt;td&gt;0.0135 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MultipleTasks&lt;/td&gt;
&lt;td&gt;12.274 s&lt;/td&gt;
&lt;td&gt;0.0073 s&lt;/td&gt;
&lt;td&gt;0.0065 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MultipleParallelTasks&lt;/td&gt;
&lt;td&gt;1.723 s&lt;/td&gt;
&lt;td&gt;0.0185 s&lt;/td&gt;
&lt;td&gt;0.0144 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OrleansTasks&lt;/td&gt;
&lt;td&gt;1.118 s&lt;/td&gt;
&lt;td&gt;0.0080 s&lt;/td&gt;
&lt;td&gt;0.0074 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Though keep in mind this can be further improved with an actual cluster of orleans silos, rather than just my one locally.&lt;/p&gt;

&lt;h3&gt;
  
  
  Orleans.SyncWork.Tests
&lt;/h3&gt;

&lt;p&gt;Unit testing project for the work exposed from the NuGet package. These tests bring up a “TestCluster” which is used for the full duration of the tests against the grains.&lt;/p&gt;

&lt;p&gt;One of the tests in particular throws 10k grains onto the cluster at once, all of which are long running (~200ms each) on my machine - more than enough time to overload the cluster if the limited concurrency task scheduler is not working along side the &lt;code&gt;SyncWork&lt;/code&gt; base class correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;So that’s it. This is quite a bit simpler of an implementation that I ended up originally making for work. The same basic idea is there, but the abstraction is quite different, if it holds up well enough, maybe using this package will be in order!&lt;/p&gt;

&lt;p&gt;You can use this package to enable your Orleans cluster to handle your long running sync work, like 10s of thousands of crypto calls!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/wi8Ez1mwRcKGI/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/wi8Ez1mwRcKGI/giphy.gif" alt="do it"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/OrleansContrib/Orleans.SyncWork" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/Orleans.SyncWork/" rel="noopener noreferrer"&gt;NuGet package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.kritner.com/tags/microsoft-orleans/" rel="noopener noreferrer"&gt;My other Orleans posts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dotnetcore.show/episode-21-orleans-with-russell-hammett/" rel="noopener noreferrer"&gt;Talking about Orleans on the dotnetcore show&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/2018/10/31/Microsoft-Orleans-Reporting-Dashboard/"&gt;Orleans Dashboard blog post&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>microsoftorleans</category>
    </item>
    <item>
      <title>Getting Started with NDepend</title>
      <dc:creator>Russ Hammett</dc:creator>
      <pubDate>Sat, 20 Nov 2021 15:00:00 +0000</pubDate>
      <link>https://forem.com/kritner/getting-started-with-ndepend-12nd</link>
      <guid>https://forem.com/kritner/getting-started-with-ndepend-12nd</guid>
      <description>&lt;p&gt;One of the folks over at &lt;a href="https://www.ndepend.com/docs/getting-started-with-ndepend"&gt;NDepend&lt;/a&gt; reached out to me to provide a license for their product; let’s do some exploring!&lt;/p&gt;

&lt;p&gt;I’ve never actually used NDepend, and have only otherwise scratched the surface using other code analysis tools. Perhaps this will be a mis-categorization, but the few I’ve worked with previously include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FxCop&lt;/li&gt;
&lt;li&gt;StyleCop&lt;/li&gt;
&lt;li&gt;VS Enterprise code analysis (I forget if this is the actual name, I no longer have an enterprise license)&lt;/li&gt;
&lt;li&gt;SonarQube&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea of tools like NDepend seems pretty straight forward, look through your source code, and identify potential pain points that could use another look. Sounds pretty simple in theory, but I’d imagine the details of such analysis goes way over my head.&lt;/p&gt;

&lt;h1&gt;
  
  
  Getting started in Visual Studio
&lt;/h1&gt;

&lt;p&gt;Visual Studio is no longer my IDE of choice when working in .net core, but I dusted it off to play around with NDepend. It was quite simple to get installed as a plugin for visual studio, and here’s a helpful video if it’s not otherwise obvious on how to get started: &lt;a href="https://www.ndepend.com/docs/getting-started-with-ndepend"&gt;https://www.ndepend.com/docs/getting-started-with-ndepend&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I didn’t really know what to expect, and I don’t have any real significant (personal) code bases that I can hook this up to, so for now I’ll run it against my &lt;a href="https://github.com/Kritner-Blogs/Kritner.PatternExamples"&gt;Design Patterns repository&lt;/a&gt; to get a baseline.&lt;/p&gt;

&lt;p&gt;To kick off the analysis I clicked the circle icon in the bottom right of my visual studio instance, and was quickly presented with a dashboard that looked like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T50Q6yXr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2021/02/16/getting-started-with-ndepend/start.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T50Q6yXr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2021/02/16/getting-started-with-ndepend/start.JPG" alt="Initial run of NDepend dashboard" width="880" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that this wasn’t &lt;em&gt;actually&lt;/em&gt; my first run, I think it was my second or third after having changed a few lines of code in between runs to get a delta in the analysis. Looking at the dashboard it became apparent the analyzer must keep “state” as to what the issues are over time; which is a great sounding feature as it will allow you to track your code “smells” as your code continues to be developed!&lt;/p&gt;

&lt;h1&gt;
  
  
  The Dashboard
&lt;/h1&gt;

&lt;p&gt;Upon first gazing at the dashboard (pictured above), I was honestly a bit overwhelmed. It was a lot of information, and I didn’t see an immediately obvious “starting point” of what I should be concerned with.&lt;/p&gt;

&lt;p&gt;Twice weekly I meet with a few friends over a video/screen share session and we work through plural sight courses together, this week I actually had them on to do a “first impressions” of the NDepend tool; unfortunately my OBS setup was hosed up, and I lost &lt;em&gt;most&lt;/em&gt; of the audio. I say this as it is not only my own “first impression” but others as well - namely several who have (slightly) used static analysis tools previously, and others who have not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sections
&lt;/h2&gt;

&lt;p&gt;Here I’ll go over what I considered to be the “sections” that were present when going through the NDepend tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dashboard
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2GIMqo-2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2021/02/16/getting-started-with-ndepend/1_dashboard.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2GIMqo-2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2021/02/16/getting-started-with-ndepend/1_dashboard.JPG" alt="Dashboard" width="880" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sorry for the awful quality of the above image, it was taken from the recording that I couldn’t end up using due to lack of audio.&lt;/p&gt;

&lt;p&gt;This seemed to be more or less the “meat and potatoes” of it all. We can see numerous metrics, some of which are pretty self explanatory, but several of which perhaps not so much for someone who is coming from a code base that has no testing; which granted is itself kind of a problem.&lt;/p&gt;

&lt;p&gt;I did make a few adjustments to the code from the previous time I ran analysis, I am a &lt;em&gt;big&lt;/em&gt; fan of tracking the code quality over time - so this is a huge benefit in my book. It was not immediately obvious to me how this information is tracked over time in a “multi developer project” situation. I am under the assumption that some of the files that were added to the project itself would need to be added to source control in order to track the information over time, but I’m not really a big fan of that (assuming my assumption is even correct). NDepend &lt;em&gt;can&lt;/em&gt; be installed in some build systems, which I would guess would keep the analysis separate from the source, but tied to that repository in some manner. I believe I saw integrations for at TFS and TeamCity, I wonder if it could also be integrated with something like TravisCI or GitHub actions.&lt;/p&gt;

&lt;p&gt;The sections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lines of code - pretty obvious what this metric is, it shows the total number of lines of code in your analyzed solution, including a delta from the last time of analysis (or when comparing to a specific baseline).&lt;/li&gt;
&lt;li&gt;Debt - based on what I (think I) know about what technical debt, this metric would go up or down based on a potential variety of factors, namely method complexity and code coverage. This was one that we all had questions around.&lt;/li&gt;
&lt;li&gt;Quality Gates, Rules, Issues - None of us were sure what separates these issues from one another, but they seemed to be driven in some manner by the “Queries and Rules” that are enabled within a separate section, but that was another point of confusion coming to this for the “first time”. The gist of these sections seemed to be based around a configurable set of rules that you can (or not) tailor to your project. On the first run through it seemed like we were getting some false positives around use of System.Random being used in a “non security setting”. Seems that rules can be enabled or disabled on a rule level, or by utilizing attributes to ignore specific NDepend rules for a particular member within your code.&lt;/li&gt;
&lt;li&gt;Types - a breakdown of the various assemblies, namespaces, files, etc. that are a part of your codebase. It would be interesting to see how this changes over time within a code base.&lt;/li&gt;
&lt;li&gt;Coverage - I did not have any coverage in this particular project, and it was definitely asked about by the folks I was doing the walk through with. The way I assume it works it “Code Coverage” as a function of unit testing and exhausting code branches. A very useful metric to have, as long as you have &lt;a href="https://dev.to/2016/02/21/what-is-the-business-value-of-unit-testing/"&gt;meaningful tests and asserts&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Method complexity - I’d imagine this is a similar metric to the &lt;a href="https://en.wikipedia.org/wiki/Cyclomatic_complexity"&gt;Cyclomatic Complexity&lt;/a&gt; that is a part of the enterprise Visual Studio analysis tool. The more complex your methods are, the harder your code is to grok and/or maintain. This metric likely plays a large part in the “Debt” metric mentioned above.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall I found the dashboard pretty useful, if not a bit overwhelming. One thing I think the dashboard could use that would benefit the users of NDepend greatly, especially when they’re just starting out, is a little “i” popup per metric:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pK-hvgrx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2021/02/16/getting-started-with-ndepend/2_baseline.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pK-hvgrx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2021/02/16/getting-started-with-ndepend/2_baseline.JPG" alt='"i" popup for "Choose Baseline"' width="880" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The popup could at a minimum describe the metric, but probably better, still link to the NDepend documentation on what exactly the metric means, and what goes into its calculation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Queries and Rules Explorer
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lEHrKiEd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2021/02/16/getting-started-with-ndepend/3_qr.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lEHrKiEd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2021/02/16/getting-started-with-ndepend/3_qr.JPG" alt="Queries and Rules Explorer" width="880" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was not really sure where to start here. When poking around with checking and unchecking things within this window, it seemed to impact what is “in scope” of the analysis. It seems like you can probably also define your own rules to look for in your code base, but we did not get into that in our first excursion into NDepend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Queries and Rules Edit
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TNK0d3B4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2021/02/16/getting-started-with-ndepend/4.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TNK0d3B4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2021/02/16/getting-started-with-ndepend/4.JPG" alt="Queries and Rules Edit" width="332" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When clicking on an action item within the dashboard be it “good or bad”, this pane was updated. In the case of failed rule checks, you’d be given a means of fixing it, with navigation to the code block that failed the check. This section was quite useful for helping get more “green lights” on the dashboard, though it was quite cramped in the tab it showed up in, though I understand &lt;em&gt;why&lt;/em&gt; it showed up there, so you could view the dashboard and/or problem code while keeping the failed rules list visible as well.&lt;/p&gt;

&lt;h1&gt;
  
  
  Review
&lt;/h1&gt;

&lt;p&gt;Overall I find the information provided through NDepend to be quite useful, having a tool like NDepend running as a part of your build process, even automatically rejecting PRs if certain quality gates aren’t met sounds fantastic.&lt;/p&gt;

&lt;p&gt;I did have a few concerns about “what exactly I’m looking at” which could be remedied via “?” or “i” links on the (granted already quite busy) interface; but I think overall would be a welcome change, especially for first time users (perhaps a feature toggle?).&lt;/p&gt;

&lt;p&gt;How does this static analysis compare with other, now free options like Roslyn analyzers, or other tools like sonar, or perhaps even ReSharper? I would imagine there’s some overlap, but that’s not necessarily a bad thing if the multitude of tools bring something unique to the table. The analysis as compared to other baselines I found very nice, though I’m curious how it works in a “multi developer” setting, and with source control and build systems.&lt;/p&gt;

&lt;p&gt;I look forward to actually implementing some tests in my analyzed project, and/or putting the tool up against something “more real” than my Patterns repository. Overall it seems like NDepend is a very powerful tool!&lt;/p&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.ndepend.com/docs/getting-started-with-ndepend"&gt;NDepend&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ndepend.com/docs/getting-started-with-ndepend"&gt;Getting Started with NDepend&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Kritner-Blogs/Kritner.PatternExamples"&gt;Design Patterns repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/2016/02/21/what-is-the-business-value-of-unit-testing/"&gt;What is the Business Value of Unit Testing?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Cyclomatic_complexity"&gt;Cyclomatic Complexity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>staticanalysis</category>
      <category>ndepend</category>
      <category>csharp</category>
    </item>
    <item>
      <title>dotnet centralized package versioning</title>
      <dc:creator>Russ Hammett</dc:creator>
      <pubDate>Fri, 08 Oct 2021 14:00:00 +0000</pubDate>
      <link>https://forem.com/kritner/dotnet-centralized-package-versioning-4i41</link>
      <guid>https://forem.com/kritner/dotnet-centralized-package-versioning-4i41</guid>
      <description>&lt;p&gt;In a &lt;a href="https://blog.kritner.com/2021/10/01/dotnet-centralized-package-versioning/"&gt;previous post&lt;/a&gt; I talked about using variable for keeping NuGet packages across multiple projects; now there is a (potentially) better way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;In the previous post, it was demonstrated how to utilize “repository wide” variables located in a Directory.Build.props folder. With these variables defined, you could use the variable, rather than a literal value, in your csproj to keep dependencies throughout the repository on the same version.&lt;/p&gt;

&lt;p&gt;There is now a feature (in review) at &lt;a href="https://github.com/NuGet/Home/wiki/Centrally-managing-NuGet-package-versions"&gt;https://github.com/NuGet/Home/wiki/Centrally-managing-NuGet-package-versions&lt;/a&gt; which allows for doing the same thing, just in a more elegant way, at least in my opinion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting it up
&lt;/h2&gt;

&lt;p&gt;The in review feature relies on a new “repository wide” like file called “Directory.Packages.props”. Using &lt;a href="https://github.com/Kritner-Blogs/DirectoryBuildProps/releases/tag/v1.1"&gt;https://github.com/Kritner-Blogs/DirectoryBuildProps/releases/tag/v1.1&lt;/a&gt; as a starting point, we’ll introduce the centralized package versioning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update Directory.Build.props
&lt;/h3&gt;

&lt;p&gt;First, we’ll update the “Directory.Build.props” file to contain the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ManagePackageVersionsCentrally&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/ManagePackageVersionsCentrally&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells visual studio, rider, the dotnet cli, etc., that the current folder, as well as its children (so repository wide if used at the root of the repo) that central package versioning is used.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Directory.Packages.props
&lt;/h3&gt;

&lt;p&gt;Within the same folder as the “Directory.Build.props”, we’ll be creating a new file “Directory.Packages.props”. This file is used to specify the NuGet package version used for all packages used across the repository. Continuing to use the “Kritner.SolarProjection”, we’ll set up our new “Directory.Packages.props” file to specify the version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Project&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;PackageVersion&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Kritner.SolarProjection"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"1.0.2"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above, we’re just defining the package as well as a version that should be used when that package is listed within any csproj file at the same level or as a child of the directory where the “Directory.Build.props” file is located.&lt;/p&gt;

&lt;p&gt;Note that the above file does &lt;strong&gt;&lt;em&gt;not&lt;/em&gt;&lt;/strong&gt; include the package automatically in csproj files, it only defines the version to use when the package is listed in the csproj file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update csproj files
&lt;/h3&gt;

&lt;p&gt;Now that central package versioning is being used in our repository, we’ll need to make updates to our csproj file to make use of it; namely removing the version declaration from our csproj files.&lt;/p&gt;

&lt;p&gt;Currently, we have the following in our csproj file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.NET.Sdk"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net5.0&lt;span class="nt"&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Kritner.SolarProjection"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"$(NuGet-Kritner-SolarProjection)"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above you’ll note that the old “variable style” version is being used, now with central package management, we don’t even need that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.NET.Sdk"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net5.0&lt;span class="nt"&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Kritner.SolarProjection"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Additional notes
&lt;/h2&gt;

&lt;p&gt;In the previous post we learned how to utilize variables to control our nuget package versions used across the repository, where desired. In this post we explored how to define a package version in one place, and for that same package version to be used across the whole repository.&lt;/p&gt;

&lt;p&gt;There are a few other things to note when using central package versioning however:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a package version is defined in the csproj while central package versioning is turned on, you will be a compile error

&lt;ul&gt;
&lt;li&gt;This is done (I believe) to help force the developer “into the pit of success”, making them aware of the fact that the package version is either already defined on a central level and you don’t need to specify the version, or that the package version needs to be defined in the “Directory.Packages.props” file.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Related to the above point, you are still able to override the package version within a csproj by making use of the “OverrideVersion” property (as opposed to “Version” property) within the csproj file, as so:
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5wVEdfpQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2021/10/01/dotnet-centralized-package-versioning/override.JPG" alt="OverrideVersion"&gt;
&lt;/li&gt;
&lt;li&gt;You can still make use of variables within your props files, as demonstrated in the above image. I like using these variables for defining a “package version” for a suite of packages, where they’re all released with a similar version number all at the same time, even one something didn’t &lt;em&gt;necessarily&lt;/em&gt; change for each package in the suite. Examples of where this can be useful include the &lt;a href="https://github.com/dotnet/aspnetcore"&gt;aspnetcore&lt;/a&gt; repository itself, as well as &lt;a href="https://github.com/dotnet/orleans"&gt;Microsoft Orleans&lt;/a&gt; - define the version of the package suite you want to use as a variable, then in your “Build.Packages.props” file, rely on that variable for each package used, rather than defining the literal package version for each package.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/NuGet/Home/wiki/Centrally-managing-NuGet-package-versions"&gt;https://github.com/NuGet/Home/wiki/Centrally-managing-NuGet-package-versions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Kritner-Blogs/DirectoryBuildProps/releases/tag/v1.1"&gt;Code at beginning of post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Kritner-Blogs/DirectoryBuildProps/releases/tag/v1.2"&gt;Code at end of post&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>programming</category>
      <category>nuget</category>
    </item>
    <item>
      <title>How to build a "tamperproof cookie"</title>
      <dc:creator>Russ Hammett</dc:creator>
      <pubDate>Tue, 17 Aug 2021 00:00:00 +0000</pubDate>
      <link>https://forem.com/kritner/how-to-build-a-tamperproof-cookie-3bj6</link>
      <guid>https://forem.com/kritner/how-to-build-a-tamperproof-cookie-3bj6</guid>
      <description>&lt;p&gt;All right! It’s been a minute! Tamperproof cookies, I needed one, it’s pretty simple after thinking it through.&lt;/p&gt;

&lt;p&gt;I feel like I &lt;em&gt;always&lt;/em&gt; say it’s been a while since my last post… but this time it has in fact been a while.&lt;/p&gt;

&lt;h1&gt;
  
  
  The problem
&lt;/h1&gt;

&lt;p&gt;So… working on a multi factor authentication setup for a freelance client. Have the multi factor one time password all set up, but now I need a way to implement a “remember me for 30 days” feature. That should be &lt;em&gt;relatively&lt;/em&gt; simple, right? Can’t we just use cookies?&lt;/p&gt;

&lt;p&gt;Not so fast there past me prior to thinking it through! Cookies can easily be modified by the client, because they’re stored on the client local storage and aren’t signed with a &lt;a href="https://searchsecurity.techtarget.com/definition/digital-signature"&gt;digital signature&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Here’s some context into the situation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_RKFcETW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2021/07/27/how-to-tamperproof-cookie/context.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_RKFcETW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2021/07/27/how-to-tamperproof-cookie/context.PNG" alt="Context from the Coding Blocks Slack"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Basically, I needed a way to store local to the user a secure (tamper proof) means of indicating the user has been dual factor authenticated for 30 days. The &lt;em&gt;simplest&lt;/em&gt; way to do this would seemingly be to store a cookie with a value of the expiration date and user name. However, this first idea I had falls apart quickly, since a user could just change the cookie value (either expiration date and/or user name) and be able to get past the second factor. The second factor one time password (OTP) entry should &lt;em&gt;only&lt;/em&gt; be presented to the user if the cookie is valid, for the specific user, and is not yet expired (30 days from the cookie creation).&lt;/p&gt;

&lt;h1&gt;
  
  
  Outline of Requirements
&lt;/h1&gt;

&lt;p&gt;There are a few things that are needed for a client side solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Something&lt;/em&gt; stored client side that can be &lt;em&gt;securely&lt;/em&gt; used as a means of indicating the user does not need to enter a OTP

&lt;ul&gt;
&lt;li&gt;Should have some sort of unique attribution to the user - either through a user ID, email address, or something similar&lt;/li&gt;
&lt;li&gt;Should have an expiration date that is taken into account when checking the validity of the thing&lt;/li&gt;
&lt;li&gt;Should not validate successfully in instances where the user information or the expiration date is changed&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  The solution
&lt;/h1&gt;

&lt;p&gt;Once documenting the above requirements coupled with the above screenshot, it became pretty clear to me that some sort of cryptography could be used to ensure the tamperproof part of the thing being stored on the client side - in this case a cookie.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/HMAC"&gt;HMAC&lt;/a&gt; - Hash-base Message Authentication Code - is a cryptographic operation that utilizes a “key” and “message” to produce a “mac” which is (more or less) the combination of the two pieces. The general hmac signature looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;key&lt;/code&gt; portion is secret between one or more parties - in my case one party, and the &lt;code&gt;message&lt;/code&gt; is data that is to be validated as genuine. In my case, the &lt;code&gt;key&lt;/code&gt; will be kept secret from the user, as that &lt;code&gt;key&lt;/code&gt; is what verifies the &lt;code&gt;message&lt;/code&gt; is what we put into the cookie.&lt;/p&gt;

&lt;p&gt;What’s a good &lt;code&gt;key&lt;/code&gt; to use you may be asking yourself? Well, it’s not the user name, though it’s something similar. The user already knows their own username, so it’s not a good candidate for a &lt;code&gt;key&lt;/code&gt;. There is however, their password, specifically their password &lt;em&gt;hash&lt;/em&gt;. The user would not know the hash of their password that is stored in the remote system, so it is a good potential candidate for a &lt;code&gt;key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The user’s password is computed basically like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;password_digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="n"&gt;plaintext_password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work_factor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;password_digest&lt;/code&gt; is the output of running &lt;code&gt;salt&lt;/code&gt; concatenated with &lt;code&gt;plaintext_password&lt;/code&gt; through a &lt;a href="https://en.wikipedia.org/wiki/One-way_function"&gt;one way function&lt;/a&gt; &lt;code&gt;H&lt;/code&gt;, &lt;code&gt;work_factor&lt;/code&gt; times. The &lt;code&gt;password_digest&lt;/code&gt; is not something the user would know, as their salt changes with each password change, nor do they know the underlying one way function applied to their &lt;code&gt;plaintext_password&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, now we have a &lt;code&gt;key&lt;/code&gt; candidate, what about the actual message? The message is thankfully very straightforward, and we can use the datetime of the OTP cookie expiration. The full HMAC call will end up being: &lt;code&gt;cookie_hash_content = hmac(H(password_hash), expiration_date_string)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This entire &lt;code&gt;cookie_hash_content&lt;/code&gt; can be stored in the body of the cookie, though we will &lt;em&gt;also&lt;/em&gt; need to stored the &lt;code&gt;expiration_date_string&lt;/code&gt; within the cookie as well, so that the server side can reconstruct the inputs into the &lt;code&gt;hmac&lt;/code&gt; call, and verify they match.&lt;/p&gt;

&lt;p&gt;Thankfully, this is easy enough to do with a pipe (or other) delimited set of fields within the cookie. I will be using this format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{expiration_date_string}|{cookie_hash_content}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validating the above cookie at authentication time is as simple as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try to split the cookie value into two pieces, if less than two pieces exist, return false&lt;/li&gt;
&lt;li&gt;Try to parse the first element in the split as a date, if fails, return false&lt;/li&gt;
&lt;li&gt;Ensure the parsed date is after the current datetime, if it is not, return false&lt;/li&gt;
&lt;li&gt;Validate the &lt;code&gt;cookie_hash_content&lt;/code&gt; by comparing the cookie’s second part of the split, to a newly generated &lt;code&gt;hmac(H(password_hash), expiration_date_string)&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;if they don’t match, return false&lt;/li&gt;
&lt;li&gt;if they match, return true&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The above pseudocode also has the added benefit if the user changes their password, they will immediately fail the OTP cookie check, as the mac using their old password hash will no longer match the newly generated/checked password hash upon login. This in a few ways makes things just a bit more secure.&lt;/p&gt;

&lt;p&gt;Here is a quick run through of the HMAC (and a few unit tests) using fake &lt;code&gt;expireDates&lt;/code&gt; and &lt;code&gt;passwordHashes&lt;/code&gt;: &lt;a href="https://replit.com/@Kritner/TamperProofCookies#main.py"&gt;https://replit.com/@Kritner/TamperProofCookies#main.py&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Final thoughts
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;This works because the &lt;code&gt;key&lt;/code&gt; is not known (or knowable) to the end user&lt;/li&gt;
&lt;li&gt;The salts, while not &lt;em&gt;necessarily&lt;/em&gt; secret, are not known to the end user, so they would have no chance of being able to recreate the same conditions to come up with the &lt;code&gt;key&lt;/code&gt; being utilized&lt;/li&gt;
&lt;li&gt;We verify the &lt;code&gt;cookie_hash_content&lt;/code&gt; by re-computing the value based on the values stored in the cookie itself, along with the additional data of &lt;code&gt;key&lt;/code&gt; that isn’t in the cookie, and can’t be known by the user, so there’s no chance the user would be able to come up with “the right information” that could pass the verify step.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://searchsecurity.techtarget.com/definition/digital-signature"&gt;Digital Signatures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/HMAC"&gt;HMAC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/One-way_function"&gt;One Way Function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://replit.com/@Kritner/TamperProofCookies#main.py"&gt;replit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.codingblocks.net/slack/"&gt;Coding blocks slack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Photo by &lt;a href="https://unsplash.com/photos/_PGlV_zMXgw"&gt;nate_dumlao&lt;/a&gt; on &lt;a href="https://unsplash.com/"&gt;Unsplash&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>cookie</category>
      <category>cryptography</category>
      <category>mfa</category>
    </item>
    <item>
      <title>Design Patterns: Decorator</title>
      <dc:creator>Russ Hammett</dc:creator>
      <pubDate>Fri, 09 Apr 2021 15:00:00 +0000</pubDate>
      <link>https://forem.com/kritner/design-patterns-decorator-6jd</link>
      <guid>https://forem.com/kritner/design-patterns-decorator-6jd</guid>
      <description>&lt;p&gt;The decorator pattern is a structural design pattern that can be used to add functionality to classes without modifying the original class or its interface.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Decorator Pattern
&lt;/h1&gt;

&lt;p&gt;From &lt;a href="https://en.wikipedia.org/wiki/Decorator_pattern" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern. The decorator pattern is structurally nearly identical to the chain of responsibility pattern, the difference being that in a chain of responsibility, exactly one of the classes handles the request, while for the decorator, all classes handle the request.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;“Prefer composition over inheritance” might be something you’ve heard of before, and I feel like the decorator pattern is basically the epitome of this saying. Decorators are themselves implementations of an abstraction, that also depend on the abstraction itself. This allows for “composable” pieces of behavior in that you would likely have a single “main” implementation of an abstraction, then separate “decorator” implementations that build on top of it.&lt;/p&gt;

&lt;p&gt;I’m not sure if that made sense, so hopefully an example will help clear things up.&lt;/p&gt;

&lt;h1&gt;
  
  
  Implementing the decorator pattern
&lt;/h1&gt;

&lt;h2&gt;
  
  
  The abstraction
&lt;/h2&gt;

&lt;p&gt;For our abstraction, we’re going to create a service that retrieves the weather for us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IWeatherGettinator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Weather&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetWeather&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Weather&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;FreedomUnits&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;ItGonRain&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Weather&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewGuid&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StringBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AppendLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The current weather:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AppendLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"\tId: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AppendLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"\tTemperature (in freedom units): &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;FreedomUnits&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AppendLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"\tIt gon rain? &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ItGonRain&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In the above, we have an interface &lt;code&gt;IWeatherGettinator&lt;/code&gt; that has a single method, which takes no arguments, and returns the weather; simple enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  The base implementation
&lt;/h2&gt;

&lt;p&gt;For this example, we’re going to pretend that to get the weather is an expensive operation, just because it makes one of the decorators of &lt;code&gt;IWeatherGettinator&lt;/code&gt; more interesting and easier to see the point of it all (IMO).&lt;/p&gt;

&lt;p&gt;So for the implementation of the &lt;code&gt;IWeatherGettinator&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WeatherGettinator&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IWeatherGettinator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Random&lt;/span&gt; &lt;span class="n"&gt;_random&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;WeatherGettinator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Random&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_random&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Gets the weather, mimics a long running task.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;see cref="Task"/&amp;gt; of &amp;lt;see cref="Weather"/&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Weather&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetWeather&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Weather&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;FreedomUnits&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;ItGonRain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On getting the weather (which is a ~5 second operation), we get a random temperature and a value that indicates if it’s going to rain or not. Each time &lt;code&gt;GetWeather&lt;/code&gt; is invoked, it takes another 5 seconds to retrieve a random “weather” instance.&lt;/p&gt;

&lt;p&gt;The above implementation should look something like this when you &lt;code&gt;ToString&lt;/code&gt; the retrieved &lt;code&gt;Weather&lt;/code&gt; via a &lt;code&gt;GetWeather&lt;/code&gt; invoke:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F12%2F24%2Fdesign-patterns-decorator%2FweatherGettinator.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F12%2F24%2Fdesign-patterns-decorator%2FweatherGettinator.JPG" alt="Weather Gettinator Output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Our first decorator
&lt;/h2&gt;

&lt;p&gt;Getting the weather seems to take a pretty long time! Perhaps we can introduce our first decorator to get some timing information on the implementation of &lt;code&gt;IWeatherGettinator&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StopWatchDecoratorWeather&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IWeatherGettinator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IWeatherGettinator&lt;/span&gt; &lt;span class="n"&gt;_weatherGettinator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;StopWatchDecoratorWeather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IWeatherGettinator&lt;/span&gt; &lt;span class="n"&gt;weatherGettinator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_weatherGettinator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weatherGettinator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Weather&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetWeather&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Stopwatch&lt;/span&gt; &lt;span class="n"&gt;sw&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Stopwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartNew&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;weather&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_weatherGettinator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetWeather&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Decorated IWeatherGettinator ran for &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ElapsedMilliseconds&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;ms"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might look a little strange, so let’s go over it a bit. The &lt;code&gt;StopWatchDecoratorWeather&lt;/code&gt; is an implementation of &lt;code&gt;IWeatherGettinator&lt;/code&gt; that also &lt;em&gt;depends&lt;/em&gt; on an implementation of &lt;code&gt;IWeatherGettinator&lt;/code&gt;. You can see that the method implementation &lt;code&gt;GetWeather&lt;/code&gt; starts a stop watch, calls the injected implementation of &lt;code&gt;IWeatherGettinator&lt;/code&gt;, stops the stopwatch, and writes to the console the length of time the operation took.&lt;/p&gt;

&lt;p&gt;Now you probably wouldn’t actually write a decorator exactly like the above, but you could do something &lt;em&gt;like&lt;/em&gt; it, especially if you were to make use of a logging framework. Logging the above could make more sense, especially if the invoke took more than a certain amount of time.&lt;/p&gt;

&lt;p&gt;How does the above get used? Well, you could “compose” your object like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;IWeatherGettinator&lt;/span&gt; &lt;span class="n"&gt;weatherGettinator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StopWatchDecoratorWeather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;WeatherGettinator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You would actually want to be constructing these objects via an IOC container or something similar, this was to just get the point across… also there is some complexity to registering decorated objects with at least the .net core built in IOC container; though you could also use a &lt;a href="https://dev.to/kritner/design-patterns-factory-4414"&gt;factory&lt;/a&gt; to accomplish it more easily.&lt;/p&gt;

&lt;p&gt;What does the above construction mean? Well, we’re instantiating a new instance of a &lt;code&gt;StopWatchDecoratorWeather&lt;/code&gt; which itself depends on a &lt;code&gt;IWeatherGettinator&lt;/code&gt; instance, in this case we’re passing in a concrete implementation of &lt;code&gt;WeatherGettinator&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Running the &lt;code&gt;IWeatherGettinator&lt;/code&gt; while making use of the &lt;code&gt;StopWatchDecoratorWeather&lt;/code&gt; would look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F12%2F24%2Fdesign-patterns-decorator%2FstopWatchDecoratorWeather.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F12%2F24%2Fdesign-patterns-decorator%2FstopWatchDecoratorWeather.JPG" alt="StopWatchDecoratorWeather Output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The issue with inheritance
&lt;/h2&gt;

&lt;p&gt;Inheritance has an issue; an issue that can be solved through composition. I’ve mentioned this several times now, but it’s kind of hard (at least for me) to convey what I mean.&lt;/p&gt;

&lt;p&gt;Say we wanted to introduce &lt;em&gt;another&lt;/em&gt; piece of functionality; in this post we’re going to be doing it through the use of decorators, but just imagine for a moment that we weren’t.&lt;/p&gt;

&lt;p&gt;In c# a class can only ever extend a single other class. If we ever wanted to “mix and match” behaviors of an abstraction that relied on inheritance, that can be extremely difficult to do. If we have an interface &lt;code&gt;IFoo&lt;/code&gt;, with an implementation &lt;code&gt;Foo&lt;/code&gt;, then had separate implementations &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt;, both of which extended &lt;code&gt;Foo&lt;/code&gt;, how would we go about introducing yet another implementation &lt;code&gt;C&lt;/code&gt;, that needed some of its own unique characteristics, as well as the additional functionality provided by both &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt;? This would be challenging with an inheritance scenario, but is really trivial when making use of decorators.&lt;/p&gt;

&lt;p&gt;To demonstrate that, let’s introduce another decorator to our &lt;code&gt;IWeatherGettinator&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The second decorator
&lt;/h2&gt;

&lt;p&gt;We’ve done some testing with our &lt;code&gt;StopWatchDecoratorWeather&lt;/code&gt; and determined that we could save a lot of time getting the weather if we introduced some caching. Thankfully, introducing caching via a decorator is very simple, and should look pretty similar to our first decorator!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CachedWeatherGettinator&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IWeatherGettinator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IWeatherGettinator&lt;/span&gt; &lt;span class="n"&gt;_weatherGettinator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Weather&lt;/span&gt; &lt;span class="n"&gt;_cachedWeather&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;_cachedOn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CachedWeatherGettinator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IWeatherGettinator&lt;/span&gt; &lt;span class="n"&gt;weatherGettinator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_weatherGettinator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weatherGettinator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Weather&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetWeather&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// this should be injected for testability, but we're demonstrating a decorator so...&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;timeDiffSecondsSinceCachedValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_cachedOn&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;TotalSeconds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_cachedWeather&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;timeDiffSecondsSinceCachedValue&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cached value does not exist or is expired, get a new one."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;_cachedOn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;_cachedWeather&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_weatherGettinator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetWeather&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Using cached value..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_cachedWeather&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our new &lt;code&gt;CachedWeatherGettinator&lt;/code&gt;, we’re making use of some instance state to return the previously retrieved weather if it’s less than 30 seconds since it was retrieved. You’ll notice this implementation, like our first decorator, both implements &lt;em&gt;and&lt;/em&gt; depends on &lt;code&gt;IWeatherGettinator&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can now try out our new decorator like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;IWeatherGettinator&lt;/span&gt; &lt;span class="n"&gt;weatherGettinator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CachedWeatherGettinator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;WeatherGettinator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you’ll see that the “first” call to the &lt;code&gt;IWeatherGettinator&lt;/code&gt; will take the 5 seconds, but another call made immediately after will return much faster.&lt;/p&gt;

&lt;p&gt;But even &lt;em&gt;more&lt;/em&gt; interesting that that, is we can make use of &lt;em&gt;multiple&lt;/em&gt; decorators for the same abstraction, the construction of which is considered composition.&lt;/p&gt;

&lt;p&gt;What could that look like?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;IWeatherGettinator&lt;/span&gt; &lt;span class="n"&gt;weatherGettinator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StopWatchDecoratorWeather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CachedWeatherGettinator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;WeatherGettinator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re “composing” our object by decorating our base &lt;code&gt;WeatherGettinator&lt;/code&gt; with multiple decorators! We have decorated the base object with both a &lt;code&gt;StopWatch&lt;/code&gt; as well as &lt;code&gt;Caching&lt;/code&gt; layer, mostly to demonstrate that the caching layer is in fact working. Let’s take a look!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F12%2F24%2Fdesign-patterns-decorator%2FcachedWeatherGettinator.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F12%2F24%2Fdesign-patterns-decorator%2FcachedWeatherGettinator.JPG" alt="StopWatch and Caching Decoration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Reasons to use this pattern
&lt;/h1&gt;

&lt;p&gt;Hopefully it should be pretty clear after going through the post why this pattern can be quite powerful, but just to reiterate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding additional functionality to an abstraction without changing the abstraction or its concretions.&lt;/li&gt;
&lt;li&gt;Keeping concerns of caching or logging separate from the base implementation. These are taken care of via decorators, allowing your core “business logic” to stay pure, and uncaring about the functionality provided by the decorators.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Decorator_pattern" rel="noopener noreferrer"&gt;Wikipedia: The Decorator Pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Kritner-Blogs/Kritner.PatternExamples/pull/2" rel="noopener noreferrer"&gt;The Decorator Pattern Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.kritner.com/categories/design-patterns/" rel="noopener noreferrer"&gt;My other posts on Design Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Photo by &lt;a href="https://unsplash.com/photos/Y_vfj8HV7GU" rel="noopener noreferrer"&gt;thetechnomaid&lt;/a&gt; on &lt;a href="https://unsplash.com/" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>designpatterns</category>
      <category>decorator</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Prettifying HealthChecks</title>
      <dc:creator>Russ Hammett</dc:creator>
      <pubDate>Fri, 04 Dec 2020 19:12:11 +0000</pubDate>
      <link>https://forem.com/kritner/prettifying-healthchecks-169g</link>
      <guid>https://forem.com/kritner/prettifying-healthchecks-169g</guid>
      <description>&lt;p&gt;Previously I wrote about creating &lt;a href="https://dev.to/kritner/microsoft-orleans-health-checks-2f5b"&gt;Health Checks for Microsoft Orleans&lt;/a&gt;, but the JSON response was too minimal. In this post we’ll see about prettifying that output!&lt;/p&gt;

&lt;p&gt;In the previous post we learned a bit about health checks, how to create them, and view their “health” from the perspective of Microsoft Orleans. The end result was a single word response of “Healthy”, “Degraded”, or “Unhealthy”; not very exciting stuff.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qN72K03Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2020/10/14/microsoft-orleans-health-checks/tada.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qN72K03Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2020/10/14/microsoft-orleans-health-checks/tada.JPG" alt="super boring response"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, I’d like to quickly go over how you’d go about not only reporting on the “overarching status”, but giving details on the individual health checks that make up that overarching status.&lt;/p&gt;

&lt;p&gt;(Note: I did have an &lt;a href="https://github.com/Kritner-Blogs/OrleansGettingStarted/issues/22"&gt;issue&lt;/a&gt; out there with a hacktoberfest label, that I did get a &lt;a href="https://github.com/Kritner-Blogs/OrleansGettingStarted/pull/24"&gt;PR&lt;/a&gt; for, but I wanted to go a &lt;em&gt;slightly&lt;/em&gt; different route in the end, though I did have it integrated into master for some time.)&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#customize-output"&gt;Health check documentation&lt;/a&gt; does go into some detail about how to accomplish this health check prettifying, but I’m not a &lt;em&gt;huge fan&lt;/em&gt; of “manually” writing out JSON; instead I opted for an anonymous object.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prettifying Property Potentials
&lt;/h1&gt;

&lt;p&gt;A few new things I want to report on from the response to the health check GET endpoint:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Health Check Name&lt;/li&gt;
&lt;li&gt;Health Check Description&lt;/li&gt;
&lt;li&gt;Individual Health Check Status&lt;/li&gt;
&lt;li&gt;Some additional information specific to the health check that describes what makes the health check return “Degraded” or “Unhealthy”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Luckily all of this information can be made available to us via the &lt;code&gt;HealthReport&lt;/code&gt; generated as a part of the health check.&lt;/p&gt;

&lt;h1&gt;
  
  
  Health Check Response Writer
&lt;/h1&gt;

&lt;p&gt;We’re going to introduce a new method that writes a custom response for our health check endpoint. From startup, we’ll want to provide a custom &lt;code&gt;ResponseWriter&lt;/code&gt; within &lt;code&gt;MapHealthChecks&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseEndpoints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoints&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"/health"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HealthCheckOptions&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;AllowCachingResponses&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;ResponseWriter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HealthCheckResponseWriter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteResponse&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AllowAnonymousAttribute&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the referenced &lt;code&gt;HealthCheckResponseWriter&lt;/code&gt; is a new static class we’ll be introducing next.&lt;/p&gt;

&lt;p&gt;the &lt;code&gt;ResponseWriter&lt;/code&gt; expects a method with the following signature:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y-NaH_Yo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2020/12/04/prettifying-healthchecks/startup.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y-NaH_Yo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2020/12/04/prettifying-healthchecks/startup.JPG" alt="ResponseWriter method signature"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll notice above the method receives an &lt;code&gt;HttpContext&lt;/code&gt; as well as &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.diagnostics.healthchecks.healthreport?view=dotnet-plat-ext-3.1"&gt;HealthReport&lt;/a&gt;. This &lt;code&gt;HealthReport&lt;/code&gt; will make available to us several pieces of data that we can report on, specific to each individual health check.&lt;/p&gt;

&lt;p&gt;As for our actual response writer implementation, here is the original that was merged into master from &lt;a href="https://github.com/L-Dogg"&gt;L-Dogg&lt;/a&gt;‘s PR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;WriteResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HealthReport&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContentType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"application/json; charset=utf-8"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;JsonWriterOptions&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Indented&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Utf8JsonWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteStartObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteStartObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"results"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteStartObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteStartObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WritePropertyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt;
                                        &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteEndObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteEndObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteEndObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteEndObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above definitely works, but I’m not huge on writing the json “manually” (if that makes sense). I wanted to write another blog post on this anyway, as I already had a branch going (and didn’t actually expect a PR :O), so here’s my solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HealthCheckResponseWriter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;WriteResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HealthReport&lt;/span&gt; &lt;span class="n"&gt;healthReport&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContentType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"application/json; charset=utf-8"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonConvert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SerializeObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;healthReport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;healthReport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;Formatting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Indented&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I find it a &lt;em&gt;bit&lt;/em&gt; more concise working with the anonymous object.&lt;/p&gt;

&lt;h1&gt;
  
  
  Health Check updates
&lt;/h1&gt;

&lt;p&gt;We’re not currently generating “data” information from the health checks that the &lt;code&gt;HealthCheckResponseWriter&lt;/code&gt; would be able to make use of, so let’s take a look at what we could do there.&lt;/p&gt;

&lt;p&gt;My intention for the “data” property of the anonymous object is to describe what would make the specific health check return a “Degraded” or “Unhealthy”, anything aside from those two statuses can be assumed to be “Healthy”.&lt;/p&gt;

&lt;p&gt;If you recall, we already built thresholds into the health checks to represent the degraded and unhealthy statuses, now we’ll just need to provide those available to the health report.&lt;/p&gt;

&lt;p&gt;Taking a look at the &lt;code&gt;HealthCheckResult&lt;/code&gt; class:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ItNVNs-L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2020/12/04/prettifying-healthchecks/healthCheckResult.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ItNVNs-L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2020/12/04/prettifying-healthchecks/healthCheckResult.JPG" alt="HealthCheckResult"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;you’ll see that method takes in an optional &lt;code&gt;IReadOnlyDictionary&amp;lt;string, object&amp;gt; data = null&lt;/code&gt;, which happens to be the “data” member we made sure to return from our &lt;code&gt;WriteResponse&lt;/code&gt; method in the previous section of the post.&lt;/p&gt;

&lt;p&gt;We will make use of this &lt;code&gt;IReadonlyDictionary&lt;/code&gt; to provide our “threshold” information on a per grain basis. I will be putting this threshold information into both the CPU and Memory grains, but just as an example here’s what one of those will look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;StatelessWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CpuHealthCheckGrain&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Grain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ICpuHealthCheckGrain&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;UnhealthyThreshold&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;DegradedThreshold&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;70&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ReadOnlyDictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;HealthCheckData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ReadOnlyDictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Unhealthy Threshold"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;UnhealthyThreshold&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Degraded Threshold"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;DegradedThreshold&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IHostEnvironmentStatistics&lt;/span&gt; &lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CpuHealthCheckGrain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IHostEnvironmentStatistics&lt;/span&gt; &lt;span class="n"&gt;hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CheckHealthAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CancellationToken&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CpuUsage&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Unhealthy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not determine CPU usage."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HealthCheckData&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CpuUsage&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;UnhealthyThreshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Unhealthy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;$"CPU utilization is unhealthy at &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CpuUsage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0.00&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HealthCheckData&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CpuUsage&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DegradedThreshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Degraded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;$"CPU utilization is degraded at &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CpuUsage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0.00&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HealthCheckData&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Healthy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;$"CPU utilization is healthy at &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CpuUsage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0.00&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HealthCheckData&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should notice in the above that we introduce a &lt;code&gt;ReadOnlyDictionary&lt;/code&gt; with the thresholds for degraded and unhealthy, then passed that &lt;code&gt;ReadOnlyDictionary&lt;/code&gt; to the &lt;code&gt;data&lt;/code&gt; parameter of the static method within &lt;code&gt;HealthCheckResult&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Testing it out
&lt;/h1&gt;

&lt;p&gt;The only thing left to do is test it out! You may have seen the cover image which contained spoilers, but just to wrap things up, here’s what it looks like when hitting the “/health” endpoint after our changes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HTxT3xLM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2020/12/04/prettifying-healthchecks/postman.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HTxT3xLM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2020/12/04/prettifying-healthchecks/postman.JPG" alt='JSON response against the "/health" endpoint'&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.60.1"&gt;Code at beginning of post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.60.2"&gt;Code at end of post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Kritner-Blogs/OrleansGettingStarted/pull/24"&gt;PR with changes from this post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/kritner/microsoft-orleans-health-checks-2f5b"&gt;Microsoft Orleans - Health Checks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.kritner.com/categories/programming/microsoft-orleans/"&gt;My other Orleans posts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#customize-output"&gt;Health check documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.diagnostics.healthchecks.healthreport?view=dotnet-plat-ext-3.1"&gt;HealthReport class&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>csharp</category>
      <category>netcore</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>Microsoft Orleans - Health Checks</title>
      <dc:creator>Russ Hammett</dc:creator>
      <pubDate>Fri, 04 Dec 2020 15:00:00 +0000</pubDate>
      <link>https://forem.com/kritner/microsoft-orleans-health-checks-2f5b</link>
      <guid>https://forem.com/kritner/microsoft-orleans-health-checks-2f5b</guid>
      <description>&lt;p&gt;Health Checks are a means of seeing how a system is running at the time of performing the check. Let’s see how we can apply them to Orleans!&lt;/p&gt;

&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-3.1" rel="noopener noreferrer"&gt;Health Checks&lt;/a&gt; are generally exposed via HTTP endpoints, and when hit (often at a “/hc” or “/health” endpoint) they are able to report of the “health” of the current system.&lt;/p&gt;

&lt;p&gt;The health checks, at least in .net land, are comprised of an enum &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.diagnostics.healthchecks.healthstatus?view=dotnet-plat-ext-3.1" rel="noopener noreferrer"&gt;HealthStatus&lt;/a&gt; which indicates Health, Degraded, or Unhealthy. The health checks themselves are created by implementing concretions of the &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.diagnostics.healthchecks.ihealthcheck?view=dotnet-plat-ext-3.1" rel="noopener noreferrer"&gt;IHealthCheck&lt;/a&gt; interface.&lt;/p&gt;

&lt;p&gt;Any system can include one or more health checks, and what “a health check” means is completely up to you as the implementer. You could as an example have 1 health check that “checks”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Foo&lt;/li&gt;
&lt;li&gt;Bar&lt;/li&gt;
&lt;li&gt;Baz&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All contained within a “single” health check called “muhSystem” (or whatever), &lt;em&gt;or&lt;/em&gt; you could implement the three above “checks” as their own individual health checks; so a single check vs multiple checks, all of which represent “the same thing”. Why would you choose one over the other? Well, going the single check route allows you to check on &lt;code&gt;Foo&lt;/code&gt;, &lt;code&gt;Bar&lt;/code&gt;, and &lt;code&gt;Baz&lt;/code&gt;es health, without “leaking” any information about what’s being checked. This sort of check could be useful if you needed to be careful about revealing information on some of the internal workings of your system.&lt;/p&gt;

&lt;p&gt;In the “three separate checks” scenario, it might not matter to you if you leak some information about your system, you want to give your users (or perhaps your &lt;a href="https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/monitor-app-health#use-watchdogs" rel="noopener noreferrer"&gt;watchdog&lt;/a&gt;) information on a more detailed level.&lt;/p&gt;

&lt;h1&gt;
  
  
  Getting set up
&lt;/h1&gt;

&lt;p&gt;We’ll be starting the code section of this post from the &lt;a href="https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.58" rel="noopener noreferrer"&gt;v0.58&lt;/a&gt; tag on my OrleansGettingStarted repository.&lt;/p&gt;

&lt;p&gt;I did not write a blog post about the changes that were performed in the update to the v0.58 tag, but one of the changes was getting the silo host running under the next &lt;code&gt;UseOrleans&lt;/code&gt; extension method. In the newer version of .net core and Orleans, you’re able to host multiple “processes” from the same &lt;code&gt;IHostBuilder&lt;/code&gt;. What this allows us to do is host a small API that will serve the health check endpoint through http requests.&lt;/p&gt;

&lt;p&gt;First thing we’ll do is add a default web host to our host builder - which after the change will be hosting both our silo host, as well as our api:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;hostBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureWebHostDefaults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseStartup&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Startup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Note there are going to be other varying changes that I may not be specifically calling out, but the end code is &lt;a href="https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.60" rel="noopener noreferrer"&gt;here&lt;/a&gt; and in the references at the bottom of the post.)&lt;/p&gt;

&lt;p&gt;We’ll also introduce a &lt;code&gt;Startup&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Startup&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Startup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// This method gets called by the runtime. Use this method to add services to the container.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IApplicationBuilder&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IWebHostEnvironment&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsDevelopment&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseDeveloperExceptionPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseHttpsRedirection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRouting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseEndpoints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoints&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above class should more or less be the “default” &lt;code&gt;Startup&lt;/code&gt; class present when creating a new web API project from template.&lt;/p&gt;

&lt;h1&gt;
  
  
  Health Checks
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Basic Health Check
&lt;/h2&gt;

&lt;p&gt;The first health check we’re going to do will be a basic one - in fact that’s what we’ll name it. For this health check, we’ll use an &lt;code&gt;IClusterClient&lt;/code&gt; and ensure it can get an instance of a grain, and get a result from that grain. If it can get a result from the grain the health check should return “Healthy”, otherwise “Unhealthy”.&lt;/p&gt;

&lt;p&gt;As always, we first need our Orleans grain interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IBasicHealthCheckGrain&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IHealthCheck&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IGrainWithGuidKey&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above is quite simple, we’re creating a class that implements both a &lt;code&gt;IHealthCheck&lt;/code&gt; and &lt;code&gt;IGrainWithGuidKey&lt;/code&gt;. &lt;code&gt;IGrainWithGuidKey&lt;/code&gt; should be familiar from some of my &lt;a href="https://blog.kritner.com/categories/programming/microsoft-orleans/" rel="noopener noreferrer"&gt;other Orleans posts&lt;/a&gt;, and the &lt;code&gt;IHealthCheck&lt;/code&gt; grain was mentioned earlier in this post, it’s an interface that describes a health check. We’re not adding anything to this interface that isn’t already provided via the &lt;code&gt;IHealthCheck&lt;/code&gt; or &lt;code&gt;IGrainWithGuidKey&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Our basic health check grain implementation looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;StatelessWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BasicHealthCheckGrain&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Grain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IBasicHealthCheckGrain&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CheckHealthAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CancellationToken&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Healthy&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it! Just return a healthy result. If our actual &lt;code&gt;IHealthCheck&lt;/code&gt; implementation is unable to get an instance of this grain and an exception is encountered, the exception handler will return “Unhealthy” for us.&lt;/p&gt;

&lt;p&gt;Now that we have a health check grain, we’ll need an actual &lt;code&gt;IHealthCheck&lt;/code&gt; implementation that will utilize our newly created “health check grain”. I know we’ll be creating several health checks here, all of which do “a lot of the same thing”, so this seems like the perfect opportunity to introduce an abstract class &lt;code&gt;OrleansHealthCheckBase&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrleansHealthCheckBase&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IHealthCheck&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IClusterClient&lt;/span&gt; &lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;OrleansHealthCheckBase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IClusterClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Entry into health check, ensures the client is initialized, if it is not returns a healthy status.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="context"&amp;gt;The health check context.&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="cancellationToken"&amp;gt;The cancellation token.&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;see cref="Task"/&amp;gt; of &amp;lt;see cref="HealthCheckResult"/&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CheckHealthAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CancellationToken&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsInitialized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Healthy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt; not yet initialized."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;CheckHealthGrainAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Perform the actual health check work within this implemented method.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="context"&amp;gt;The health check context.&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="cancellationToken"&amp;gt;The cancellation token.&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;see cref="Task"/&amp;gt; of &amp;lt;see cref="HealthCheckResult"/&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CheckHealthGrainAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of our health checks will depend on a connection to the cluster, so the above will take in a &lt;code&gt;IClusterClient&lt;/code&gt;, and ensure the cluster is initialized prior to proceeding with the to be implemented actual check.&lt;/p&gt;

&lt;p&gt;As I mentioned previously, for our “Basic” health check, we’ll just be checking that we can get an instance of a grain, and return a value. Such a health check will look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BasicOrleansHealthCheck&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;OrleansHealthCheckBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;BasicOrleansHealthCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IClusterClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CheckHealthGrainAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetGrain&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IBasicHealthCheckGrain&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CheckHealthAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Unhealthy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Health check failed."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance Health Checks
&lt;/h2&gt;

&lt;p&gt;Now that the basic health check is out of the way, we can implement some more meaningful ones. The following health checks require a registered &lt;code&gt;IHostEnvironmentStatistics&lt;/code&gt; (which you can find out more about &lt;a href="https://blog.kritner.com/2019/02/25/microsoft-orleans-dashboard-update-cpu-memory-stats/" rel="noopener noreferrer"&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;These health checks will be especially useful for gauging the utilization of an Orleans node over time, which would allow you to make decisions on questions such as “should I spin up or down additional nodes for this cluster?”. Answers to such questions, especially if running your Orleans cluster in a k8s environment, are much simpler when you have performance metrics exposed via a health check endpoint, and are making use of a watchdog.&lt;/p&gt;

&lt;p&gt;Going to go through these fast, they should be mostly self explanatory but you can view the completed code for anything I don’t specifically cover.&lt;/p&gt;

&lt;h3&gt;
  
  
  CPU Health Check
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ICpuHealthCheckGrain&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IHealthCheck&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IGrainWithGuidKey&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;StatelessWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CpuHealthCheckGrain&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Grain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ICpuHealthCheckGrain&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;UnhealthyThreshold&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;DegradedThreshold&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;70&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IHostEnvironmentStatistics&lt;/span&gt; &lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CpuHealthCheckGrain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IHostEnvironmentStatistics&lt;/span&gt; &lt;span class="n"&gt;hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CheckHealthAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CancellationToken&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CpuUsage&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;UnhealthyThreshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Unhealthy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;$"CPU utilization is unhealthy at &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CpuUsage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%."&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CpuUsage&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DegradedThreshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Degraded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;$"CPU utilization is degraded at &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CpuUsage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%."&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Healthy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;$"CPU utilization is healthy at &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CpuUsage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%."&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;New grain interface, new grain implementation for CPU health checking. We’re going to return Unhealthy if above 90% CPU, Degraded if above 70%, Healthy otherwise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory Health Check
&lt;/h3&gt;

&lt;p&gt;Same basic idea for the memory health check, again making use of our registered &lt;code&gt;IHostEnvironmentStatistics&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IMemoryHealthCheckGrain&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IHealthCheck&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IGrainWithGuidKey&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;StatelessWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemoryHealthCheckGrain&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Grain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IMemoryHealthCheckGrain&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;UnhealthyThreshold&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;95&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;DegradedThreshold&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IHostEnvironmentStatistics&lt;/span&gt; &lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MemoryHealthCheckGrain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IHostEnvironmentStatistics&lt;/span&gt; &lt;span class="n"&gt;hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CheckHealthAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CancellationToken&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;AvailableMemory&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;TotalPhysicalMemory&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Unhealthy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not determine memory calculation."&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;AvailableMemory&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;AvailableMemory&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Unhealthy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not determine memory calculation."&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;memoryUsed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AvailableMemory&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;_hostEnvironmentStatistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalPhysicalMemory&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memoryUsed&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;UnhealthyThreshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Unhealthy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;$"Memory utilization is unhealthy at &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;memoryUsed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0.00&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%."&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memoryUsed&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DegradedThreshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Degraded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;$"Memory utilization is degraded at &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;memoryUsed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0.00&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%."&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Healthy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;$"Memory utilization is healthy at &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;memoryUsed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0.00&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%."&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, I’m going a slightly different route and returning “Unhealthy” if the memory information cannot be determined, this should probably be consistently done between this and the CPU health check, but I wanted to show how &lt;em&gt;you&lt;/em&gt; as the implementer is able to choose what “Healthy” vs “Unhealthy” means. For this memory health check, we’re unhealthy if above 95% memory utilization, degraded if above 90, healthy otherwise.&lt;/p&gt;

&lt;h1&gt;
  
  
  Wiring up the health checks
&lt;/h1&gt;

&lt;p&gt;Now that we have our health check grains, we’ll introduce new &lt;code&gt;IHealthChecks&lt;/code&gt; very similar to the &lt;code&gt;BasicOrleansHealthCheck&lt;/code&gt; which extended &lt;code&gt;OrleansHealthCheckBase&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CpuOrleansHealthCheck&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OrleansHealthCheckBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CpuOrleansHealthCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IClusterClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CheckHealthGrainAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetGrain&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ICpuHealthCheckGrain&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CheckHealthAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Unhealthy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Health check failed."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemoryOrleansHealthCheck&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OrleansHealthCheckBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MemoryOrleansHealthCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IClusterClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CheckHealthGrainAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthCheckContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetGrain&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMemoryHealthCheckGrain&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CheckHealthAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Unhealthy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Health check failed."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to wire all of these health checks up to our “/health” endpoint within our webhost. Luckily, this is pretty easy. The earlier &lt;code&gt;Startup&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F10%2F14%2Fmicrosoft-orleans-health-checks%2FpreChangeStartup.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F10%2F14%2Fmicrosoft-orleans-health-checks%2FpreChangeStartup.JPG" alt="Pre-change Startup.cs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Startup&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Startup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// This method gets called by the runtime. Use this method to add services to the container.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHealthChecks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddCheck&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BasicOrleansHealthCheck&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"basicOrleans"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddCheck&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CpuOrleansHealthCheck&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"cpuOrleans"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddCheck&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MemoryOrleansHealthCheck&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"memoryOrleans"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IApplicationBuilder&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IWebHostEnvironment&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsDevelopment&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseDeveloperExceptionPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseHttpsRedirection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRouting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseEndpoints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoints&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/health"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;WithMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AllowAnonymousAttribute&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference being we’re adding the health checks (and giving them names) within &lt;code&gt;ConfigureServices&lt;/code&gt;, and mapping the health checks to a “/health” endpoint within &lt;code&gt;Configure&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Testing it out
&lt;/h1&gt;

&lt;p&gt;Let’s fire up the silo, and test this thing out!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F10%2F14%2Fmicrosoft-orleans-health-checks%2FhealthCheck.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F10%2F14%2Fmicrosoft-orleans-health-checks%2FhealthCheck.JPG" alt="Health Check breakpoint"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F10%2F14%2Fmicrosoft-orleans-health-checks%2Ftada.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F10%2F14%2Fmicrosoft-orleans-health-checks%2Ftada.JPG" alt="tada!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, that was pretty anticlimactic… we’ll have to see about prettying up that health check response, hopefully in another post that I’ll totally write real soon!&lt;/p&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.58" rel="noopener noreferrer"&gt;Code at beginning of post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.60" rel="noopener noreferrer"&gt;Code at end of post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.kritner.com/categories/programming/microsoft-orleans/" rel="noopener noreferrer"&gt;My other Orleans posts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.kritner.com/2019/02/25/microsoft-orleans-dashboard-update-cpu-memory-stats/" rel="noopener noreferrer"&gt;Microsoft Orleans - Dashboard Update - CPU/Memory Stats&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-3.1" rel="noopener noreferrer"&gt;Health Checks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.diagnostics.healthchecks.healthstatus?view=dotnet-plat-ext-3.1" rel="noopener noreferrer"&gt;HealthStatus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.diagnostics.healthchecks.ihealthcheck?view=dotnet-plat-ext-3.1" rel="noopener noreferrer"&gt;IHealthCheck&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/monitor-app-health#use-watchdogs" rel="noopener noreferrer"&gt;Watchdog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>csharp</category>
      <category>microsoftorleans</category>
      <category>netcore</category>
    </item>
    <item>
      <title>Self Notes - React Router Getting Started</title>
      <dc:creator>Russ Hammett</dc:creator>
      <pubDate>Tue, 08 Sep 2020 13:30:33 +0000</pubDate>
      <link>https://forem.com/kritner/self-notes-react-router-getting-started-6fn</link>
      <guid>https://forem.com/kritner/self-notes-react-router-getting-started-6fn</guid>
      <description>&lt;p&gt;My notes about trying to get started with React.&lt;/p&gt;

&lt;p&gt;I’m &lt;em&gt;mostly&lt;/em&gt; a backend-dev. The bit of front-end dev I’ve done was on web-forms with asp.net. Getting started in this whole ecosystem seems pretty daunting coming from a background where I have access to strongly typed, easily testable code.&lt;/p&gt;

&lt;p&gt;This is probably rehashing things I’ve stated before, but with front end I feel like it’s more of an “opinion” on if something is implemented in an “good” way.&lt;/p&gt;

&lt;p&gt;Anyway, I’m trying to mess about with React. I want to hopefully at some point, replace the mostly placeholder site I have at kritner.com, with something a bit more substantial.&lt;/p&gt;

&lt;p&gt;The first thing I thought would make sense to get started with is a general shared layout for pages.&lt;/p&gt;

&lt;p&gt;I’ll be making use of &lt;a href="https://codesandbox.io/"&gt;Code Sandbox&lt;/a&gt; for my experimentation. Code sandbox, if you’ve never used it, allows for a playground (or sandbox…) for putting together sample apps with varying tech stacks like React, Vue, with or without TypeScript, etc.&lt;/p&gt;

&lt;h1&gt;
  
  
  Stubbed Components
&lt;/h1&gt;

&lt;p&gt;Starting out with the template &lt;a href="https://codesandbox.io/s/react-typescript-react-ts"&gt;React Typescript&lt;/a&gt;. First thing I’ll do is introduce a few page stubs that my navigation will link to:&lt;/p&gt;

&lt;p&gt;About.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;About&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt; &lt;span class="nx"&gt;me&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Contact.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Contact&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Home.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above I’m utilizing function components, rather than class components &lt;a href="https://reactjs.org/docs/components-and-props.html#function-and-class-components"&gt;https://reactjs.org/docs/components-and-props.html#function-and-class-components&lt;/a&gt; as they seem to be preferred unless otherwise needed; they look a lot simpler as well.&lt;/p&gt;

&lt;h1&gt;
  
  
  Routing
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Url Routing
&lt;/h2&gt;

&lt;p&gt;Not sure if this is the best place to introduce routing (I’d guess not), but not knowing a whole lot about the conventions/standards of React yet, this is what I’m going with. I’m going to update the &lt;code&gt;App.tsx&lt;/code&gt; to include routing.&lt;/p&gt;

&lt;p&gt;The starting code for App is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt; &lt;span class="nx"&gt;CodeSandbox&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Start&lt;/span&gt; &lt;span class="nx"&gt;editing&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;see&lt;/span&gt; &lt;span class="nx"&gt;some&lt;/span&gt; &lt;span class="nx"&gt;magic&lt;/span&gt; &lt;span class="nx"&gt;happen&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For routing, I’ll pull in the “react-router-dom” package into code sandbox:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hdhvkgr4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2020/09/08/self-notes-react-router-getting-started/addDep.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hdhvkgr4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2020/09/08/self-notes-react-router-getting-started/addDep.jpg" alt="Add react-routing-dom dependency" width="512" height="829"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ll now add routing to our App component, which supports URL routing. first our imports (there are a few extra that we’ll be getting to):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BrowserRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Also importing the components that will be routed to&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./pages/Home&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Contact&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./pages/Contact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;About&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./pages/About&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’ll be making use of &lt;a href="https://reactrouter.com/core/api/Router"&gt;BrowserRouter&lt;/a&gt;, &lt;a href="https://reactrouter.com/core/api/Switch"&gt;Switch&lt;/a&gt;, and &lt;a href="https://reactrouter.com/core/api/Route"&gt;Route&lt;/a&gt; for the URL routing. The App component will need to be updated to route to our earlier created “Home”, “About”, and “Contact” components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;BrowserRouter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;exact&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/contact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Contact&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Switch&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/BrowserRouter&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above should look pretty straightforward, though the “exact” on the root route seems to be necessary, at least in the order I have the routes defined. It seems that “/contact” matches first on “/“, and would show the Home component rather than Contact, unless the exact is specified (or if the “/“ is the last route defined).&lt;/p&gt;

&lt;p&gt;Now the application should be responsive to the routes defined above:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n1GRFLTB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2020/09/08/self-notes-react-router-getting-started/urlRouting.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n1GRFLTB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.kritner.com/2020/09/08/self-notes-react-router-getting-started/urlRouting.png" alt="URL based routing" width="880" height="670"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigation Routing
&lt;/h2&gt;

&lt;p&gt;Next to handle link based navigation, we’ll make use of &lt;a href="https://reactrouter.com/web/api/Link"&gt;Link&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/contact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Contact&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full &lt;code&gt;App.jsx&lt;/code&gt; now looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BrowserRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./pages/Home&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Contact&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./pages/Contact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;About&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./pages/About&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./styles.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;BrowserRouter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/contact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Contact&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;exact&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/contact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Contact&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Switch&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/BrowserRouter&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I tried playing around with some of the routing portions being their own component, but was getting errors around trying to include things outside of a router. I’m guessing this can still refactored a bit, and am curious how others do it!&lt;/p&gt;

&lt;p&gt;Obviously the above has no styling or anything of that nature, which is something else I need to learn more about ^^&lt;/p&gt;




&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codesandbox.io/s/kritner-react-browserrouter-fefnx"&gt;Finished CodeSandbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://reactjs.org/docs/components-and-props.html#function-and-class-components"&gt;ReactJS documentation Function and Class Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://reactrouter.com/core/api/Router"&gt;React Router&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>tutorial</category>
      <category>react</category>
    </item>
    <item>
      <title>Setting up mTLS and Kestrel (cont.)</title>
      <dc:creator>Russ Hammett</dc:creator>
      <pubDate>Mon, 17 Aug 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/kritner/setting-up-mtls-and-kestrel-cont-3nkn</link>
      <guid>https://forem.com/kritner/setting-up-mtls-and-kestrel-cont-3nkn</guid>
      <description>&lt;p&gt;In the &lt;a href="https://blog.kritner.com/2020/07/15/setting-up-mtls-and-kestrel/" rel="noopener noreferrer"&gt;last post&lt;/a&gt; we started talking about mTLS. In the post I pointed out that the client cert’s signing CA was not verified, let’s fix that!&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;The main thing accomplished in the &lt;a href="https://blog.kritner.com/2020/07/15/setting-up-mtls-and-kestrel/" rel="noopener noreferrer"&gt;previous post&lt;/a&gt; was getting a CA up and running with a client certificate signed by that CA. We updated our web api to require client certificates, and successfully connected to the application using our new client certificate.&lt;/p&gt;

&lt;p&gt;The flaw in the previous post was though we were validating a client certificate was present, we weren’t actually validating that the client certificate was signed by the CA we created. If the client certificate were valid for some other reason (like if it were signed by another trusted root CA on the system, like an actual internet CA) it would still “get in”.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps to Remedy
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Set up for depicting the problem&lt;/li&gt;
&lt;li&gt;Demonstrate the problem&lt;/li&gt;
&lt;li&gt;Solve the problem with code&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Set up for depicting the problem
&lt;/h3&gt;

&lt;p&gt;So first things first, we need to show that this is in fact a problem. I’ve already described the problem, but how can we go about proving it?&lt;/p&gt;

&lt;p&gt;Easiest thing IMO, is to just create another CA and client cert with the new CA. The web api should accept both the original “client” cert, as well as the “badClient” cert.&lt;/p&gt;

&lt;p&gt;I’m going to do all the steps from the previous post for setting up the CA, including trusting our “badCa”. The reason this is being done has already been covered above, but just to reiterate, we want to show that &lt;em&gt;any&lt;/em&gt; valid cert is currently getting in; where we want &lt;em&gt;only valid certs from our intended CA&lt;/em&gt; to get in.&lt;/p&gt;

&lt;p&gt;So now we should have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A “good” CA - the one we want to ensure checked out client certs&lt;/li&gt;
&lt;li&gt;A “good” client cert - the one that should be accepted via our app&lt;/li&gt;
&lt;li&gt;A “bad” CA - a valid CA, but not one that should be allowed to sign certificates that can get into our app&lt;/li&gt;
&lt;li&gt;A “bad” client cert - signed by the “bad” ca, should &lt;em&gt;not&lt;/em&gt; (but currently will be able to) get into our app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’ll look something like this if you’re inspecting the certificates:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2Fcerts.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2Fcerts.JPG" alt="So many certs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Demonstrate the problem
&lt;/h3&gt;

&lt;p&gt;Now all there is to do is hit our web api with both the “good” cert and the “bad” cert, and confirm we are in fact able to get output from both.&lt;/p&gt;

&lt;p&gt;Just like previously, let’s make sure the web api is running with a &lt;code&gt;dotnet run&lt;/code&gt; from the “Kritner.Mtls” project.&lt;/p&gt;

&lt;p&gt;Then hit the application first w/o a cert:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --insecure -v https://localhost:5001/weatherForecast
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FpreChangeNoCert.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FpreChangeNoCert.JPG" alt="No cert"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the “good” cert:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --insecure -v --key client.key --cert client.crt https://localhost:5001/weatherForecast
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FpreChangeGoodCert.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FpreChangeGoodCert.JPG" alt="Good cert"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the “bad” cert:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --insecure -v --key badClient.key --cert badClient.crt https://localhost:5001/weatherForecast
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FpreChangeBadCert.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FpreChangeBadCert.JPG" alt="Good cert"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;You can see, as expected, the request w/o a client cert is rejected, and the requests with both the “good” and “bad” client certs get through. Now, we need to figure out how to go about restricting the app to only accept client certificates signed by our “good” CA&lt;/p&gt;

&lt;h3&gt;
  
  
  Solve the problem with code
&lt;/h3&gt;

&lt;p&gt;So first things first, we need to identify something about what makes “good” client certificate “good”, and what makes “bad” client certificate “bad”. If you inspect the certificates on your system, you’ll see there is an “Authority Key Identifier” as an attribute. This “Authority Key Identifier” on the client certificate matches the &lt;a href="https://knowledge.digicert.com/solution/SO18140#AKI" rel="noopener noreferrer"&gt;“Authority Key Identifier”&lt;/a&gt; and/or &lt;a href="https://knowledge.digicert.com/solution/SO18140#SKI" rel="noopener noreferrer"&gt;“Subject Key Identifier”&lt;/a&gt; on the CA that signed the certificate:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FauthorityKeyIdentifier.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FauthorityKeyIdentifier.JPG" alt="Authority Key Identifier"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apologies about all different CA labels and whatnot if you’ve noticed them in the screenshots, I’m switching around computers like a madlad!&lt;/p&gt;

&lt;p&gt;In the above, you’ll be able to see that the “good” cert “belongs” to the “good” CA, and the “bad” cert “belongs” to the “bad” CA - this is the information we need! Now we just need a way to get to the information in code.&lt;/p&gt;

&lt;h4&gt;
  
  
  Introduce an additional service to validate the CA
&lt;/h4&gt;

&lt;p&gt;Note the starting point of the code I’m working with is &lt;a href="https://github.com/Kritner-Blogs/Kritner.Mtls/releases/tag/v0.9.1" rel="noopener noreferrer"&gt;https://github.com/Kritner-Blogs/Kritner.Mtls/releases/tag/v0.9.1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s review the current code within &lt;code&gt;Startup&lt;/code&gt; I even left a little note for myself and others from the last post:&lt;/p&gt;

&lt;h5&gt;
  
  
  Implementation Stub
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;OnCertificateValidated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Startup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// You should implement a service that confirms the certificate passed in&lt;/span&gt;
    &lt;span class="c1"&gt;// was signed by the root CA.&lt;/span&gt;

    &lt;span class="c1"&gt;// Otherwise, a certificate that is valid to one of the other trusted CAs on the webserver,&lt;/span&gt;
    &lt;span class="c1"&gt;// would be valid in this case as well.&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You did it my dudes!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s introduce a service into this section of code, its abstraction will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ICertificateAuthorityValidator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X509Certificate2&lt;/span&gt; &lt;span class="n"&gt;clientCert&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above abstraction, we’re taking in a client certificate, and returning whether or not it’s valid (obviously). the &lt;code&gt;context&lt;/code&gt; within &lt;code&gt;OnCertificateValidated&lt;/code&gt; has access to the &lt;code&gt;ClientCertificate&lt;/code&gt; and it’s already in the form of &lt;code&gt;X509Certificate2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s stub out our implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CertificateAuthorityValidator&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ICertificateAuthorityValidator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CertificateAuthorityValidator&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CertificateAuthorityValidator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CertificateAuthorityValidator&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X509Certificate2&lt;/span&gt; &lt;span class="n"&gt;clientCert&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Validating certificate within the &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CertificateAuthorityValidator&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above is obviously just a “starting point”, where we’re always saying its valid. We’ll wire it up by registering it as a service, and plugging it into our &lt;code&gt;OnCertificateValidated&lt;/code&gt;. The writing up of the services I’ve covered several times in other posts but if you need help, take a look at the finished code (TODO put a link here… if I miss this on my review, there’ll probably be a link at the bottom).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;OnCertificateValidated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Startup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Within the OnCertificateValidated portion of Startup"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;caValidator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ICertificateAuthorityValidator&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;caValidator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientCertificate&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;failValidationMsg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"The client certificate failed to validate"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogWarning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failValidationMsg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failValidationMsg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above, we’re getting an instance of our &lt;code&gt;ICertificateAuthorityValidator&lt;/code&gt; once the client certificate is (otherwise) validated, then running out additional validation procedure on it. If the validation fails, it will mark the validation as failure, otherwise it will still be successful.&lt;/p&gt;

&lt;p&gt;With our stubbed implementation returning &lt;code&gt;true&lt;/code&gt; from &lt;code&gt;IsValid&lt;/code&gt;, let’s see what that looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FimplStubTrue.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FimplStubTrue.JPG" alt="Success with our IsValid stub always returning true"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Changing the stubbed implementation to return &lt;code&gt;false&lt;/code&gt; from &lt;code&gt;IsValid&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FimplStubFalse.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FimplStubFalse.JPG" alt="Failure with our IsValid stub always returning false"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Actual Implementation
&lt;/h5&gt;

&lt;p&gt;Now we can work on our actual implementation of the &lt;code&gt;CertificateAuthorityValidator&lt;/code&gt;. You’ll recall that we can (hopefully) rely on the “Authority Key Identifier” to ensure only our intended CA’s signed certificates can make it through validation.&lt;/p&gt;

&lt;p&gt;Shall we do some debugging?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FauthorityKeyIdentifierDebug.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FauthorityKeyIdentifierDebug.JPG" alt="Authority Key Identifier within clientCert"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The screenshot above shows that the information we need is in fact present in the data presented to us from the &lt;code&gt;X509Certificate2&lt;/code&gt;. To save a bit of time and writing, know that the “raw data” on this extension does represent the same value on the CA cert, but there’s a few additional bytes of information, namely “KeyID=” (as seen in the screenshots earlier). I could not actually get this data from the bytes to confirm (tried getting the byte string as ascii, utf8, and several others), but that’s what it seemed to be. This means for our implementation, we need the “raw data” from this extension, minus a few of the first bytes to account for what I can only assume is “KeyID=”.&lt;/p&gt;

&lt;p&gt;The full &lt;code&gt;CertificateAuthorityValidator&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CertificateAuthorityValidator&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ICertificateAuthorityValidator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CertificateAuthorityValidator&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// this should probably be injected via config or loaded from the cert&lt;/span&gt;
    &lt;span class="c1"&gt;// Apparently the bytes are in the reverse order when using this BigInteger parse method,&lt;/span&gt;
    &lt;span class="c1"&gt;// hence the reverse&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;_caCertSubjectKeyIdentifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BigInteger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"e9be86f64eb53bc12c1b5fe0f63df450274811da"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Globalization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NumberStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HexNumber&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToByteArray&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Reverse&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;AuthorityKeyIdentifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Authority Key Identifier"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CertificateAuthorityValidator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CertificateAuthorityValidator&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X509Certificate2&lt;/span&gt; &lt;span class="n"&gt;clientCert&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Validating certificate within the &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CertificateAuthorityValidator&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clientCert&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;clientCert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Oid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FriendlyName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AuthorityKeyIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;try&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;authorityKeyIdentifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_caCertSubjectKeyIdentifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                    &lt;span class="c1"&gt;// Copy from the extension raw data, starting at the index that should be after the "KeyID=" bytes&lt;/span&gt;
                    &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RawData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RawData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;_caCertSubjectKeyIdentifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                        &lt;span class="n"&gt;authorityKeyIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                        &lt;span class="n"&gt;authorityKeyIdentifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_caCertSubjectKeyIdentifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SequenceEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authorityKeyIdentifier&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Successfully validated the certificate came from the intended CA."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="k"&gt;else&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Client cert with subject '&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;clientCert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;' not signed by our CA."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"'&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;clientCert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;' did not contain the extension to check for CA validity."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FfixGoodCert.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FfixGoodCert.JPG" alt="Good cert test"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --insecure -v --key badClient.key --cert badClient.crt https://localhost:5001/weatherForecast
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FfixBadCert.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kritner.com%2F2020%2F07%2F22%2Fsetting-up-mtls-and-kestrel-cont%2FfixBadCert.JPG" alt="Bad cert test"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --insecure -v --key badClient.key --cert badClient.crt https://localhost:5001/weatherForecast
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.kritner.com/2020/07/15/setting-up-mtls-and-kestrel/" rel="noopener noreferrer"&gt;Setting up mTLS and Kestrel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://knowledge.digicert.com/solution/SO18140#AKI" rel="noopener noreferrer"&gt;What extensions and details are included in a SSL certificate?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Kritner-Blogs/Kritner.Mtls/releases/tag/v.0.9.1" rel="noopener noreferrer"&gt;Starting point of code at beginning of post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Kritner-Blogs/Kritner.Mtls/releases/tag/v1.0" rel="noopener noreferrer"&gt;Ending point of code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Photo by &lt;a href="https://unsplash.com/photos/tqxzgJlDbZg" rel="noopener noreferrer"&gt;blackwood_castle&lt;/a&gt; on &lt;a href="https://unsplash.com/" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>csharp</category>
      <category>netcore</category>
      <category>tutorial</category>
      <category>security</category>
    </item>
  </channel>
</rss>
