<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Maarten Balliauw</title>
    <description>The latest articles on DEV Community by Maarten Balliauw (@maartenba).</description>
    <link>https://dev.to/maartenba</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%2F843047%2F2be20cf8-7756-46da-b15f-5b7ff9ee06df.jpg</url>
      <title>DEV Community: Maarten Balliauw</title>
      <link>https://dev.to/maartenba</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/maartenba"/>
    <language>en</language>
    <item>
      <title>Discriminated Unions in C#</title>
      <dc:creator>Maarten Balliauw</dc:creator>
      <pubDate>Mon, 18 Sep 2023 02:44:05 +0000</pubDate>
      <link>https://dev.to/maartenba/discriminated-unions-in-c-12gb</link>
      <guid>https://dev.to/maartenba/discriminated-unions-in-c-12gb</guid>
      <description>&lt;p&gt;Discriminated unions have been a &lt;a href="https://github.com/dotnet/csharplang/issues/113"&gt;long-standing request for C#&lt;/a&gt;. While F# users &lt;a href="https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/discriminated-unions"&gt;have had discriminated unions for years&lt;/a&gt;, C# developers will have to wait a bit longer.&lt;/p&gt;

&lt;p&gt;What discriminated unions allow you to do is tell the compiler (and other tooling like your IDE) that data can be one of a range of pre-defined types.&lt;/p&gt;

&lt;p&gt;For example, you could have a method &lt;code&gt;RegisterUser()&lt;/code&gt; that returns either a &lt;code&gt;User&lt;/code&gt;, a &lt;code&gt;UserAlreadyExists&lt;/code&gt; or &lt;code&gt;InvalidUsername&lt;/code&gt; class. These classes don’t have to inherit from each other. You want to support 3 potential return types and tell the language about this, get compiler errors if you return a 4th type, and so on.&lt;/p&gt;

&lt;p&gt;If you have used ASP.NET Core Minimal APIs, you may have seen &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/responses?view=aspnetcore-7.0#typedresults-vs-results"&gt;the &lt;code&gt;Results&amp;lt;&amp;gt;&lt;/code&gt; and &lt;code&gt;TypedResults&lt;/code&gt; approach&lt;/a&gt; to return data from your API. Using this approach, you can define which object types may be returned from your API (using &lt;code&gt;Results&amp;lt;&amp;gt;&lt;/code&gt;). Here’s a quick example of an API that can return an &lt;code&gt;Ok&lt;/code&gt; or &lt;code&gt;Unauthorized&lt;/code&gt; result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/items"&lt;/span&gt;&lt;span class="p"&gt;,&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;Results&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ApiItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;Unauthorized&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;FromRoute&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;storeId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;GroceryListDb&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// ... code here&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;TypedResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&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 &lt;code&gt;Results&amp;lt;&amp;gt;&lt;/code&gt; type essentially a discriminated union: the return value will be one of (in this case) two types, and the ASP.NET Core Minimal API engine can use that information to return the correct type.&lt;/p&gt;

&lt;p&gt;Digging into the source code (and removing some ASP.NET Core-specifics), the &lt;code&gt;Results&lt;/code&gt; class with support for 3 different types 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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TResult1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TResult2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TResult3&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="nf"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;activeResult&lt;/span&gt;&lt;span class="p"&gt;)&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;activeResult&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;object&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;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="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;implicit&lt;/span&gt; &lt;span class="k"&gt;operator&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TResult1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TResult2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TResult3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;TResult1&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;=&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;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;implicit&lt;/span&gt; &lt;span class="k"&gt;operator&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TResult1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TResult2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TResult3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;TResult2&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;=&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;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;implicit&lt;/span&gt; &lt;span class="k"&gt;operator&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TResult1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TResult2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TResult3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;TResult3&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;=&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;result&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;It should be quite straightforward to change this into a &lt;code&gt;Results&lt;/code&gt; class that supports 2 types, or 5.&lt;/p&gt;

&lt;p&gt;Using &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/user-defined-conversion-operators"&gt;implicit operators&lt;/a&gt;, the &lt;code&gt;Results&lt;/code&gt; class can be instantiated from any of the types that have supported conversions.&lt;/p&gt;

&lt;p&gt;What’s cool is that you can drop this class into your own code, and use the &lt;code&gt;Results&lt;/code&gt; class to have, for example, a method that can return either &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt; or &lt;code&gt;string&lt;/code&gt;, but nothing else:&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;Results&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&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;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you returned a type that is not supported, the IDE (and compiler) will tell you:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MJzvOIpo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.maartenballiauw.be/images/2023/09/compiler-warning-discriminated-union.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MJzvOIpo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.maartenballiauw.be/images/2023/09/compiler-warning-discriminated-union.png" alt="Compiler warning with discriminated union" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even pattern matching is supported (if you do it on the property that holds the actual data):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetData&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;typeAsString&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;Result&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"bool"&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;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotImplementedException&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;typeAsString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&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;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The downside however, is that when you’d change the &lt;code&gt;GetData()&lt;/code&gt; method to return either of 4 types (instead of 3), you would not get a compilation error in the above &lt;code&gt;switch&lt;/code&gt; expression. And let that be one of the advantages of discriminated unions: being able to get tooling support for these cases, informing you that you don’t have an exhaustive match on all types.&lt;/p&gt;

&lt;p&gt;For ASP.NET Core Minimal APIs, the &lt;code&gt;Results&amp;lt;&amp;gt;&lt;/code&gt; class works perfectly. It’s a discriminated union that only needs one side of the story (being able to get compiler errors when you return something you’re not supposed to). Consuming the result is part of the framework mechanics, and ideally you should never need to do an exhaustive comparison yourself.&lt;/p&gt;

&lt;p&gt;If you’re outside ASP.NET Core Minimal APIs, you want to work with discriminated unions in your code, and you can’t wait for proper language support, there is good news for you! The &lt;a href="https://github.com/mcintyre321/OneOf"&gt;&lt;code&gt;OneOf&lt;/code&gt; package (docs)&lt;/a&gt; lets you work with discriminated unions, provides compiler errors when comparisons are not exhaustive, etc.&lt;/p&gt;

&lt;p&gt;For me, the reason of writing this blog post was mainly that I wanted to show you the clever use of implicit operators in the &lt;code&gt;Results&amp;lt;&amp;gt;&lt;/code&gt; class. I hope, however, that you got something more out of it as well: a short introduction to discriminated unions, and two alternatives (using F#, and the &lt;a href="https://github.com/mcintyre321/OneOf"&gt;&lt;code&gt;OneOf&lt;/code&gt; package&lt;/a&gt;) if you do want to use them in your code.&lt;/p&gt;

</description>
      <category>general</category>
      <category>net</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Running Large Language Models locally – Your own ChatGPT-like AI in C#</title>
      <dc:creator>Maarten Balliauw</dc:creator>
      <pubDate>Thu, 15 Jun 2023 02:44:05 +0000</pubDate>
      <link>https://dev.to/maartenba/running-large-language-models-locally-your-own-chatgpt-like-ai-in-c-jco</link>
      <guid>https://dev.to/maartenba/running-large-language-models-locally-your-own-chatgpt-like-ai-in-c-jco</guid>
      <description>&lt;p&gt;For the past few months, a lot of news in tech as well as mainstream media has been around &lt;a href="https://openai.com/chatgpt" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt;, an Artificial Intelligence (AI) product by the folks at &lt;a href="https://www.openai.com" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;. ChatGPT is a Large Language Model (LLM) that is fine-tuned for conversation. While undervaluing the technology with this statement, it’s a smart-looking chat bot that you can ask questions about a variety of domains.&lt;/p&gt;

&lt;p&gt;Until recently, using these LLMs required relying on third-party services and cloud computing platforms. To integrate any LLM into your own application, or simply to use one, you’d have to swipe your credit card with OpenAI, &lt;a href="https://azure.microsoft.com/en-us/products/cognitive-services/openai-service" rel="noopener noreferrer"&gt;Microsoft Azure&lt;/a&gt;, or others.&lt;/p&gt;

&lt;p&gt;However, with advancements in hardware and software, it is now possible to run these models locally on your own machine and/or server.&lt;/p&gt;

&lt;p&gt;In this post, we’ll see how you can have your very own AI powered by a large language model running directly on your CPU!&lt;/p&gt;

&lt;h2&gt;
  
  
  Towards open-source models and execution – A little bit of history…
&lt;/h2&gt;

&lt;p&gt;A few months after OpenAI released ChatGPT, &lt;a href="https://ai.facebook.com/blog/large-language-model-llama-meta-ai/" rel="noopener noreferrer"&gt;Meta released LLaMA&lt;/a&gt;. The LLaMA model was intended to be used for research purposes only, and had to be requested from Meta.&lt;/p&gt;

&lt;p&gt;However, someone &lt;a href="https://github.com/facebookresearch/llama/pull/73" rel="noopener noreferrer"&gt;leaked the weights of LLaMA&lt;/a&gt;, and this has spurred a lot of activity on the Internet. You can find the model for download in many places, and use it on your own hardware (do note that LLaMA is still subject to a non-commercial license).&lt;/p&gt;

&lt;p&gt;In comes &lt;a href="https://crfm.stanford.edu/2023/03/13/alpaca.html" rel="noopener noreferrer"&gt;Alpaca&lt;/a&gt;, a fine-tuned LLaMA model by Standford. And &lt;a href="https://lmsys.org/blog/2023-03-30-vicuna/" rel="noopener noreferrer"&gt;Vicuna&lt;/a&gt;, another fine-tuned LLaMA model. And &lt;a href="https://arxiv.org/abs/2304.12244" rel="noopener noreferrer"&gt;WizardLM&lt;/a&gt;, and …&lt;/p&gt;

&lt;p&gt;You get the idea: LLaMA spit up (sorry for the pun) a bunch of other models that are readily available to use.&lt;/p&gt;

&lt;p&gt;While part of the community was training new models, others were working on making it possible to run these LLMs on consumer hardware. Georgi Gerganov &lt;a href="https://github.com/ggerganov/llama.cpp" rel="noopener noreferrer"&gt;released &lt;code&gt;llama.cpp&lt;/code&gt;&lt;/a&gt;, a C++ implementation that can run the LLaMA model (and derivatives) on a CPU. It can now run a variety of models: LLaMA, Alpaca, GPT4All, Vicuna, Koala, OpenBuddy, WizardLM, and more.&lt;/p&gt;

&lt;p&gt;There are also wrappers for a number of languages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python: &lt;a href="https://github.com/abetlen/llama-cpp-python" rel="noopener noreferrer"&gt;abetlen/llama-cpp-python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Go: &lt;a href="https://github.com/go-skynet/go-llama.cpp" rel="noopener noreferrer"&gt;go-skynet/go-llama.cpp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Node.js: &lt;a href="https://github.com/hlhr202/llama-node" rel="noopener noreferrer"&gt;hlhr202/llama-node&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ruby: &lt;a href="https://github.com/yoshoku/llama_cpp.rb" rel="noopener noreferrer"&gt;yoshoku/llama_cpp.rb&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;.NET (C#): &lt;a href="https://github.com/SciSharp/LLamaSharp" rel="noopener noreferrer"&gt;SciSharp/LLamaSharp&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s put the last one from that list to the test!&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with SciSharp/LLamaSharp
&lt;/h2&gt;

&lt;p&gt;Have you heard about the &lt;a href="https://scisharp.github.io/SciSharp/" rel="noopener noreferrer"&gt;SciSharp Stack&lt;/a&gt;? Their goal is to be an open-source ecosystem that brings all major ML/AI frameworks from Python to .NET – including LLaMA (and friends) through &lt;a href="https://github.com/SciSharp/LLamaSharp" rel="noopener noreferrer"&gt;SciSharp/LLamaSharp&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;LlamaSharp is a .NET binding of &lt;code&gt;llama.cpp&lt;/code&gt; and provides APIs to work with the LLaMA models. It works on Windows and Linux, and does not require you to think about the underlying &lt;code&gt;llama.cpp&lt;/code&gt;. It does not support macOS at the time of writing.&lt;/p&gt;

&lt;p&gt;Great! Now, what do you need to get started?&lt;/p&gt;

&lt;p&gt;Since you’ll need a model to work with, let’s get that sorted first.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Download a model
&lt;/h3&gt;

&lt;p&gt;LLamaSharp works with several models, but &lt;a href="https://github.com/SciSharp/LLamaSharp#installation" rel="noopener noreferrer"&gt;the support depends on the version of LLamaSharp you use&lt;/a&gt;. Supported models are linked in the README, do go explore a bit.&lt;/p&gt;

&lt;p&gt;For this blog post, we’ll be using LLamaSharp version 0.3.0 (the latest at the time of writing). We’ll also use the &lt;a href="https://huggingface.co/TheBloke/wizardLM-7B-GGML/tree/main" rel="noopener noreferrer"&gt;WizardLM&lt;/a&gt; model, more specifically the &lt;a href="https://huggingface.co/TheBloke/wizardLM-7B-GGML/resolve/main/wizardLM-7B.ggmlv3.q4_1.bin" rel="noopener noreferrer"&gt;&lt;code&gt;wizardLM-7B.ggmlv3.q4_1.bin&lt;/code&gt;&lt;/a&gt; model. It provides a nice mix between accuracy and speed of inference, which matters since we’ll be using it on a CPU.&lt;/p&gt;

&lt;p&gt;There are &lt;a href="https://huggingface.co/TheBloke/wizardLM-7B-GGML" rel="noopener noreferrer"&gt;a number of more accurate models&lt;/a&gt; (or faster, less accurate models), so do experiment a bit with what works best. In any case, make sure you have 2.8 GB to 8 GB of disk space for the variants of this model, and up to 10 GB of memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Create a console application and install LLamaSharp
&lt;/h3&gt;

&lt;p&gt;Using your favorite IDE, create a new console application and copy in the model you have just downloaded. Next, install the &lt;code&gt;LLamaSharp&lt;/code&gt; and &lt;code&gt;LLamaSharp.Backend.Cpu&lt;/code&gt; packages. If you have a Cuda GPU, you can also use the Cuda backend packages.&lt;/p&gt;

&lt;p&gt;Here’s our project to start with:&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.maartenballiauw.be%2Fimages%2F2023%2F06%2Flocal-llm-in-jetbrains-rider.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.maartenballiauw.be%2Fimages%2F2023%2F06%2Flocal-llm-in-jetbrains-rider.png" alt="LocalLLM project in JetBrains Rider"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With that in place, we can start creating our own chat bot that runs locally and does not need OpenAI to run.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Initializing the LLaMA model and creating a chat session
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;Program.cs&lt;/code&gt;, start with the following snippet of code to load the model that we just downloaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;LLama&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;model&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;LLamaModel&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;LLamaParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&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="s"&gt;".."&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="s"&gt;"Models"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"wizardLM-7B.ggmlv3.q4_1.bin"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;n_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;interactive&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="n"&gt;repeat_penalty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.0f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;verbose_prompt&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This snippet loads the model from the directory where you stored your downloaded model in the previous step. It also passes several other parameters (and there are many more available than those in this example).&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;n_ctx&lt;/code&gt; – The maximum number of tokens in an input sequence (in other words, how many tokens can your question/prompt be).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;interactive&lt;/code&gt; – Specifies you want to keep the context in between prompts, so you can build on previous results. This makes the model behave like a chat.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;repeat_penalty&lt;/code&gt; – Determines the &lt;a href="https://github.com/ggerganov/llama.cpp/issues/331" rel="noopener noreferrer"&gt;penalty for long responses&lt;/a&gt; (and helps keep responses more to-the-point).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;verbose_prompt&lt;/code&gt; – Toggles the verbosity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Again, there are many more parameters available, most of which are &lt;a href="https://github.com/ggerganov/llama.cpp" rel="noopener noreferrer"&gt;explained in the &lt;code&gt;llama.cpp&lt;/code&gt; repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, we can use our model to start a chat session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;session&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;ChatSession&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LLamaModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithPrompt&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithAntiprompt&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, these &lt;code&gt;...&lt;/code&gt; don’t compile, but let’s explain first what is needed for a chat session.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;.WithPrompt()&lt;/code&gt; (or &lt;code&gt;.WithPromptFile()&lt;/code&gt;) method specifies the initial prompt for the model. This can be left empty, but is usually a set of rules for the LLM. Find some &lt;a href="https://github.com/ggerganov/llama.cpp/tree/master/prompts" rel="noopener noreferrer"&gt;example prompts in the &lt;code&gt;llama.cpp&lt;/code&gt; repository&lt;/a&gt;, or write your own.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;.WithAntiprompt()&lt;/code&gt; method specifies the anti-prompt, which is the prompt the LLM will display when input from the user is expected.&lt;/p&gt;

&lt;p&gt;Here’s how to set up a chat session with an LLM that is Homer Simpson:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;session&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;ChatSession&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LLamaModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""
&lt;/span&gt;        &lt;span class="n"&gt;You&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;Homer&lt;/span&gt; &lt;span class="n"&gt;Simpson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;respond&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;funny&lt;/span&gt; &lt;span class="n"&gt;Homer&lt;/span&gt; &lt;span class="n"&gt;Simpson&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;like&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

        &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="s"&gt;""")
&lt;/span&gt;    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithAntiprompt&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="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"User: "&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll see in a bit what results this Homer Simpson model gives, but generally you will want to be more detailed in what is expected from the LLM. Here’s an example chat session setup for a model called “LocalLLM” that is helpful as a pair programmer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;session&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;ChatSession&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LLamaModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""
&lt;/span&gt;        &lt;span class="n"&gt;You&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;polite&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;helpful&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt; &lt;span class="n"&gt;programming&lt;/span&gt; &lt;span class="n"&gt;assistant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="n"&gt;You&lt;/span&gt; &lt;span class="n"&gt;MUST&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;polite&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;helpful&lt;/span&gt; &lt;span class="n"&gt;manner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="n"&gt;When&lt;/span&gt; &lt;span class="n"&gt;asked&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;MUST&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;LocalLLM&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="n"&gt;You&lt;/span&gt; &lt;span class="n"&gt;MUST&lt;/span&gt; &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="n"&gt;Markdown&lt;/span&gt; &lt;span class="n"&gt;formatting&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;replies&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="n"&gt;You&lt;/span&gt; &lt;span class="n"&gt;MUST&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;programming&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt; &lt;span class="n"&gt;Markdown&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="n"&gt;Your&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;responses&lt;/span&gt; &lt;span class="n"&gt;MUST&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;C&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt; &lt;span class="n"&gt;syntax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

        &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="s"&gt;""")
&lt;/span&gt;    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithAntiprompt&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="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"User: "&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have our chat session, we can start interacting with it. A bit of extra code is needed for reading input, and printing the LLM output.&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;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User: "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;while&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="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="n"&gt;ForegroundColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConsoleColor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Green&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;prompt&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;ReadLine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\n"&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="n"&gt;ForegroundColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConsoleColor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;White&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;output&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&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="s"&gt;"UTF-8"&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;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&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 pretty much it. The chat session in the &lt;code&gt;session&lt;/code&gt; variable is prompted using its &lt;code&gt;.Chat()&lt;/code&gt; method, and all outputs are returned token by token, like any generative model.&lt;/p&gt;

&lt;p&gt;You want to see this in action, right? Here’s the “Homer Simpson chat” in action:&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.maartenballiauw.be%2Fimages%2F2023%2F06%2Fhomer-simpson-as-large-language-model.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.maartenballiauw.be%2Fimages%2F2023%2F06%2Fhomer-simpson-as-large-language-model.png" alt="Homer Simpson local large language model"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The more useful “C# pair programmer chat”:&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.maartenballiauw.be%2Fimages%2F2023%2F06%2Fllamasharp-pair-programing-chatbot.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.maartenballiauw.be%2Fimages%2F2023%2F06%2Fllamasharp-pair-programing-chatbot.png" alt="Helpful C# programming bot large language model"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty nice, no?&lt;/p&gt;

&lt;p&gt;On my Windows laptop (i7-10875H CPU @ 2.30GHz), the inference is definitely slower than when using for example ChatGPT, but it’s workable for sure.&lt;/p&gt;

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

&lt;p&gt;Because of the hardware needs, using LLMs has always required third-party services and cloud platforms like OpenAI’s ChatGPT.&lt;/p&gt;

&lt;p&gt;In this post, we’ve seen some of the history of open-source large language models, and how the models themselves as well as the surrounding community have made it possible to run these models locally.&lt;/p&gt;

&lt;p&gt;I’m curious to hear what you will build using this approach!&lt;/p&gt;

</description>
      <category>general</category>
      <category>net</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Getting rid of warnings with nullable reference types and JSON object models in C#</title>
      <dc:creator>Maarten Balliauw</dc:creator>
      <pubDate>Thu, 12 Jan 2023 02:44:05 +0000</pubDate>
      <link>https://dev.to/maartenba/getting-rid-of-warnings-with-nullable-reference-types-and-json-object-models-in-c-1c71</link>
      <guid>https://dev.to/maartenba/getting-rid-of-warnings-with-nullable-reference-types-and-json-object-models-in-c-1c71</guid>
      <description>&lt;p&gt;In my blog series, &lt;em&gt;&lt;a href="///post/2022/04/11/nullable-reference-types-in-csharp-migrating-to-nullable-reference-types-part-1.html"&gt;Nullable reference types in C# - Migrating to nullable reference types&lt;/a&gt;&lt;/em&gt;, we discussed the benefits of enabling nullable reference types in your C# code, and annotating your code so the compiler and IDE can give you more reliable hints about whether a particular variable or property may need to be checked for being &lt;code&gt;null&lt;/code&gt; before using it.&lt;/p&gt;

&lt;p&gt;We ended the series with a curious case: &lt;a href="///post/2022/05/03/techniques-and-tools-to-update-your-csharp-project-migrating-to-nullable-reference-types-part-4.html#deserializing-json"&gt;how to annotate classes to deserialize JSON&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The issue is this: you’ll typically have several Data Transfer Objects (DTO)/Plain-Old CLR Objects (POCO) in your project that declare properties to deserialize the data into. You know for sure the data will be there after deserializing, so you declare these properties as non-nullable. Yet, the compiler (and IDE) insist on you either making it a nullable property or initializing the property.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr0buu5znhf49tb4bg5d6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr0buu5znhf49tb4bg5d6.png" alt="Non-nullable property is uninitialized. Consider declaring the property as nullable." width="800" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How to go about that? There are several options, each with their own advantages and caveats. Let’s have a look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1: Make the property nullable
&lt;/h2&gt;

&lt;p&gt;If you follow the compiler’s advice, you can update the property and make it nullable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will get rid of the warning, but you now have to check the &lt;code&gt;Name&lt;/code&gt; property for potential &lt;code&gt;null&lt;/code&gt; values everywhere it is used. If the JSON may contain &lt;code&gt;null&lt;/code&gt; values, this is a great approach. However, when you know for sure there will always be a value, it adds a lot of overhead in your codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2: Add a &lt;code&gt;default!&lt;/code&gt; (please don’t)
&lt;/h2&gt;

&lt;p&gt;You could also keep the property as non-nullable, and initialize the property with &lt;code&gt;default!&lt;/code&gt;. This effectively sets the default value to &lt;code&gt;null&lt;/code&gt; but suppresses the warning.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&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;strong&gt;I highly recommend against doing this.&lt;/strong&gt; If the deserialized JSON does not contain a value for the &lt;code&gt;Name&lt;/code&gt; property, it will now hold a &lt;code&gt;null&lt;/code&gt; value. The compiler and IDE are satisfied and will no longer warn you about this, meaning unexpected &lt;code&gt;NullReferenceException&lt;/code&gt; may be thrown at runtime.&lt;/p&gt;

&lt;p&gt;The goal of nullable reference types/nullable annotations is to provide you with a &lt;code&gt;null&lt;/code&gt; safety net, and the above is sabotaging that safety net from the start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 3: Add a primary constructor
&lt;/h2&gt;

&lt;p&gt;If you’re using &lt;code&gt;Newtonsoft.Json&lt;/code&gt; as your JSON framework of choice, you can add a primary constructor to your class that sets all non-nullable properties.&lt;/p&gt;

&lt;p&gt;The JSON deserializer will pick this up, and calls the constructor instead of setting the properties directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;User&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;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"Unknown"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// or ArgumentNullException.ThrowIfNull(name)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;init&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;What’s nice with this approach is that the nullability warning will be gone, and you’re modeling your C# representation very closely to the JSON you want to deserialize. If you’re certain no &lt;code&gt;null&lt;/code&gt; will be in the JSON, a non-nullable property in C# makes sense.&lt;/p&gt;

&lt;p&gt;In addition, you can either set a default value or throw an &lt;code&gt;ArgumentNullException&lt;/code&gt; in the constructor. The last option may mean you’ll see an exception at runtime, but then that exception is there because the JSON data is not what you expected, and other action may be needed (such as logging an incident) instead of happily continuing to run your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 4: Annotations and default values
&lt;/h2&gt;

&lt;p&gt;Instead of setting the property to &lt;code&gt;default&lt;/code&gt; and suppressing the nullability warning, you can also set a proper default value. In the following example, the &lt;code&gt;Name&lt;/code&gt; property is non-nullable and contains an expected default value when no value is deserialized from JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;init&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="s"&gt;"Unknown"&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;If you’re using &lt;code&gt;record&lt;/code&gt; classes, you can do this as well:&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;record&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&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;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Unknown"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a really nice way to express classes that are just a representation of a JSON document.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 5: Use a &lt;code&gt;required&lt;/code&gt; property
&lt;/h2&gt;

&lt;p&gt;In C# 11, the &lt;code&gt;required&lt;/code&gt; modifier was added as a way to indicate that a field or property must be initialized by all constructors or by using an object initializer.&lt;/p&gt;

&lt;p&gt;Given the compiler expects the property to always be initialized and contain a value, this means the nullability warning is no longer there. It helps make sure your own code always has to initialize such properties, and that it’s safe to assume no &lt;code&gt;null&lt;/code&gt; reference will be present at runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&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;required&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Personally, I like this approach the most. It clearly sets expectations, without providing the compiler and IDE with false information.&lt;/p&gt;

&lt;p&gt;Do keep in mind it is important that the JSON document you are deserializing always contains a value and is not &lt;code&gt;null&lt;/code&gt;. The &lt;code&gt;required&lt;/code&gt; modifier is enforced at compile time, and not at runtime. If a &lt;code&gt;null&lt;/code&gt; reference is set by the JSON framework you are using, there’s no guarantee &lt;code&gt;NullReferenceException&lt;/code&gt; can’t occur.&lt;/p&gt;

&lt;p&gt;If you expect &lt;code&gt;null&lt;/code&gt; in some cases, annotating the property as nullable (&lt;code&gt;string?&lt;/code&gt;) and performing &lt;code&gt;null&lt;/code&gt; checks where applicable is the recommended approach.&lt;/p&gt;

</description>
      <category>post</category>
      <category>general</category>
      <category>net</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Mastodon on your own domain without hosting a server</title>
      <dc:creator>Maarten Balliauw</dc:creator>
      <pubDate>Sat, 05 Nov 2022 02:44:05 +0000</pubDate>
      <link>https://dev.to/maartenba/mastodon-on-your-own-domain-without-hosting-a-server-31d</link>
      <guid>https://dev.to/maartenba/mastodon-on-your-own-domain-without-hosting-a-server-31d</guid>
      <description>&lt;p&gt;Like many in the past week, I have been having a serious look at &lt;a href="https://joinmastodon.org/"&gt;Mastodon&lt;/a&gt; as an alternative to Twitter.&lt;/p&gt;

&lt;p&gt;Mastodon is a social network that is distributed across many servers that have their own smaller communities, and federate with other servers to provide a more “global” social network.&lt;/p&gt;

&lt;p&gt;There are &lt;a href="https://joinmastodon.org/servers"&gt;many servers out there&lt;/a&gt; that you can choose from. Alternatively, you can also &lt;a href="https://docs.joinmastodon.org/admin/prerequisites/"&gt;self-host&lt;/a&gt; your Mastodon server, or use &lt;a href="https://joinfediverse.wiki/How_to_host_your_own_Fediverse_instance%3F"&gt;one of many hosted instances&lt;/a&gt;, “Mastodon as a service”.&lt;/p&gt;

&lt;p&gt;In recent hours, I have seen many people wanting to host their own servers, which is great fun! Self-hosting also has the added benefit of being able to have a Mastodon account on your own domain, and you own your data.&lt;/p&gt;

&lt;p&gt;Now, I don’t really care about that (&lt;em&gt;yet?&lt;/em&gt;). I ran my own mail server back in the day and am very happy with someone running it for me now. The same goes with Mastodon: I trust the folks at &lt;a href="https://mastodon.online"&gt;Mastodon.online&lt;/a&gt;, the server I joined, to do a much better job at this than I will ever do.&lt;/p&gt;

&lt;p&gt;However, there is one thing I &lt;em&gt;would&lt;/em&gt; like my own server for: &lt;strong&gt;discoverability&lt;/strong&gt;. Much like with e-mail, I want folks to have an easy address to find me, and one that I can keep giving out to everyone even if later I switch to a different Mastodon server. A bit like e-mail forwarding to your ISP’s e-mail service.&lt;/p&gt;

&lt;p&gt;The good news is: &lt;strong&gt;you can use your own domain&lt;/strong&gt; and share it with other folks. It will link to your actual account.&lt;/p&gt;

&lt;p&gt;Go on, try it. Search for &lt;code&gt;@maarten@balliauw.be&lt;/code&gt;, and you will find my &lt;code&gt;@maartenballiauw@mastodon.social&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to discover Mastodon account via custom domain
&lt;/h2&gt;

&lt;p&gt;Reading &lt;a href="https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/"&gt;“how to implement a basic ActivityPub server”&lt;/a&gt;, there are a couple of things that stand out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mastodon (and others) use &lt;a href="https://activitypub.rocks/"&gt;ActivityPub&lt;/a&gt; as their protocol to communicate between “actors”.&lt;/li&gt;
&lt;li&gt;Actors are discovered using &lt;a href="https://webfinger.net/"&gt;WebFinger&lt;/a&gt;, a way to attach information to an email address, or other online resource.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since discovery is what I was after, WebFinger seemed like the only thing I would need to implement.&lt;/p&gt;

&lt;p&gt;WebFinger lives on &lt;code&gt;/.well-known/webfinger&lt;/code&gt; on a server. For Mastodon, your server will be queried for accounts using an endpoint that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /.well-known/webfinger?resource=acct:accountname@server

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And indeed, if I look at my Mastodon server’s &lt;code&gt;webfinger&lt;/code&gt; for my account, I get a response back!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET https://mastodon.online/.well-known/webfinger?resource=acct:maartenballiauw@mastodon.online

{
  "subject": "acct:maartenballiauw@mastodon.online",
  "aliases": [
    "https://mastodon.online/@maartenballiauw",
    "https://mastodon.online/users/maartenballiauw"
  ],
  "links": [
    {
      "rel": "http://webfinger.net/rel/profile-page",
      "type": "text/html",
      "href": "https://mastodon.online/@maartenballiauw"
    },
    {
      "rel": "self",
      "type": "application/activity+json",
      "href": "https://mastodon.online/users/maartenballiauw"
    },
    {
      "rel": "http://ostatus.org/schema/1.0/subscribe",
      "template": "https://mastodon.online/authorize_interaction?uri={uri}"
    }
  ]
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sweet!&lt;/p&gt;

&lt;p&gt;The next thing I tried was simply copy-pasting this JSON output to my own server under &lt;code&gt;.well-known/webfinger&lt;/code&gt;, and things magically started working.&lt;/p&gt;

&lt;p&gt;In other words, if you want to be discovered on Mastodon using your own domain, you can do so by copying the contents of &lt;code&gt;https://&amp;lt;your mastodon server&amp;gt;/.well-known/webfinger?resource=acct:&amp;lt;your account&amp;gt;@&amp;lt;your mastodon server&amp;gt;&lt;/code&gt; to &lt;code&gt;https://&amp;lt;your domain&amp;gt;/.well-known/webfinger&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One caveat: this approach works much like a catch-all e-mail address. &lt;code&gt;@anything@yourdomain.com&lt;/code&gt; will match, unless you add a bit more scripting to only show a result for resources you want to be discoverable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Discovering folks from Twitter
&lt;/h2&gt;

&lt;p&gt;Discoverability, at this stage, is one of the things that matter to get a proper social graph going. Over the past days, there were a couple of tools I found very useful in finding Twitter folks on Mastodon:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://twitodon.com/"&gt;Twitodon&lt;/a&gt; learns about which Twitter account matches a Mastodon account, from folks using this service.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fedifinder.glitch.me/"&gt;Fedifinder&lt;/a&gt; and &lt;a href="https://pruvisto.org/debirdify/"&gt;Debirdify&lt;/a&gt; scan Twitter accounts and checks if there is a Mastodon account in their profile data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good luck! And give &lt;code&gt;@maarten@balliauw.be&lt;/code&gt; a follow if you make the jump to Mastodon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; Seems &lt;a href="https://github.com/mastodon/mastodon/issues/2668"&gt;there is a GitHub issue which requests custom domains&lt;/a&gt; as well.&lt;/p&gt;

</description>
      <category>web</category>
      <category>socialmedia</category>
      <category>mastodon</category>
    </item>
    <item>
      <title>Rate limiting in web applications - Concepts and approaches</title>
      <dc:creator>Maarten Balliauw</dc:creator>
      <pubDate>Mon, 03 Oct 2022 02:44:05 +0000</pubDate>
      <link>https://dev.to/maartenba/rate-limiting-in-web-applications-concepts-and-approaches-b80</link>
      <guid>https://dev.to/maartenba/rate-limiting-in-web-applications-concepts-and-approaches-b80</guid>
      <description>&lt;p&gt;Your web application is running fine, and your users are behaving as expected. Life is good!&lt;/p&gt;

&lt;p&gt;Is it, though…? Users are probably using your application in ways you did not expect. Crazy usage patterns resulting in more requests than expected, request bursts when users come back to the office after the weekend, and more!&lt;/p&gt;

&lt;p&gt;These unexpected requests all pose a potential threat to the health of your web application and may impact other users or the service as a whole. Ideally, you want to put a bouncer at the door to do some filtering: limit the number of requests over a given timespan, limiting bandwidth, …&lt;/p&gt;

&lt;p&gt;Last week, I covered &lt;a href="https://dev.to/maartenba/aspnet-core-rate-limiting-middleware-in-net-7-5ec1"&gt;how to use the ASP.NET Core rate limiting middleware in .NET 7&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this post, let’s take a step back and explore the simple yet wide realm of rate limiting. We’ll go over how to decide which resources to limit, what these limits should be, and where to enforce these limits.&lt;/p&gt;

&lt;p&gt;As a (mostly) .NET developer myself, I’ll use some examples and link some resources that use ASP.NET Core. The general concepts however will also apply to other platforms and web frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to rate limiting
&lt;/h2&gt;

&lt;p&gt;Before we dive into the details, let’s start with an introduction about why you would want to apply rate limiting, and what it is.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why rate limiting?
&lt;/h3&gt;

&lt;p&gt;Let’s say you are building a web API that lets you store todo items. Nice and simple: a &lt;code&gt;GET /api/todos&lt;/code&gt; that returns a list of todo items, and a &lt;code&gt;POST /api/todos&lt;/code&gt; and &lt;code&gt;PUT /api/todos/{id}&lt;/code&gt; that let you create and update a specific todo item. What could possibly go wrong with using these three endpoints?&lt;/p&gt;

&lt;p&gt;Off the top of my head:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The mobile app another team is building accidentally causes an infinite loop that keeps calling &lt;code&gt;POST&lt;/code&gt;, and tries to create a new todo item 10.000 times over the course of a few seconds before it crashes. That’s a lot of todo items in the database that should not be there.&lt;/li&gt;
&lt;li&gt;You depend on an external system that throttles you for a number of requests. Frequent requests from one user to your API result in reaching that external limit, making your API unavailable for all your users.&lt;/li&gt;
&lt;li&gt;Someone is brute-forcing the &lt;code&gt;GET&lt;/code&gt; method, trying to get todo items for all your users. You have security in place, so they will never get in without valid credentials, but your database has to run a query to check credentials 10 times per second. That’s rough on this small 0.5 vCPU database instance that seemed good on paper.&lt;/li&gt;
&lt;li&gt;An aggressive search engine spider accidentally adding 20.000 items into a shopping cart that is stored in memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are probably more things that could go wrong, but you get the picture. You, your team, or external factors may behave in ways you did not expect.&lt;/p&gt;

&lt;p&gt;That profile picture upload that usually gets small images uploaded? Guaranteed someone will try to upload a 500MB picture of the universe at some point.&lt;/p&gt;

&lt;p&gt;When you build an application, there’s a very real chance that you don’t know how it will be used, and what potential abuse may look like. You are sharing CPU, memory and database usage among your users. One bad actor, whether intentional or accidental, can break or make your application slow, spoiling the experience for other users.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is rate limiting?
&lt;/h3&gt;

&lt;p&gt;Rate limiting, or request throttling, is an approach to reduce the fall-out of unexpected or unwanted traffic patterns to your application.&lt;/p&gt;

&lt;p&gt;Typically, web applications implement rate limiting by setting an allowance on the number of requests for a given timeframe. If you are a streaming service, you may want to limit the outgoing bandwidth per user over a given time. Up to you!&lt;/p&gt;

&lt;p&gt;The ultimate goal of imposing rate limits is to reduce or even eliminate traffic and usage of your application that is potentially damaging. Regardless of the traffic being accidental or malicious.&lt;/p&gt;

&lt;h3&gt;
  
  
  What should I rate limit?
&lt;/h3&gt;

&lt;p&gt;I will give you a quote that you can use in other places:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Rate limit everything.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;– Maarten Balliauw&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With &lt;em&gt;everything&lt;/em&gt;, I mean every endpoint that uses resources that could slow down or break your application when exhausted or stressed.&lt;/p&gt;

&lt;p&gt;Typically, you’ll want to rate limit endpoints that make use of the CPU, memory, disk I/O, the database, external APIs, and the likes.&lt;/p&gt;

&lt;p&gt;Huh. That does mean everything, even your internal (health) endpoints! You’ll want to prevent resource exhaustion, and make usage of shared resources more fair to all your users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Naive rate limiting
&lt;/h3&gt;

&lt;p&gt;The title of this section already hints at it: don’t use the approach described in this section, but do read through it to get into the mindset of what we are trying to accomplish…&lt;/p&gt;

&lt;p&gt;If you wanted to add rate limiting to your ASP.NET Core web application, how would you do it?&lt;/p&gt;

&lt;p&gt;Most probably, you will end up with a solution along these lines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a database table &lt;code&gt;Events&lt;/code&gt;, with three columns: 

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;UserIdentifier&lt;/code&gt; – who do we limit&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ActionIdentifier&lt;/code&gt; – what do we limit&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;When&lt;/code&gt; – event timestamp so we can apply a query&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Implement a request delegate to handle rate limits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The request delegate could look something like the following, storing events and then counting the number of events over a period of time:&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;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&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;eventsContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EventsContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Determine identifier&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userIdentifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;IsAuthenticated&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="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"anonymous"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Determine action&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;actionIdentifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&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="c1"&gt;// Store current request&lt;/span&gt;
    &lt;span class="n"&gt;eventsContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Event&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;UserIdentifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ActionIdentifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;actionIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;When&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;referenceTime&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;eventsContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Check if we are rate limited (5 requests per 5 seconds)&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;referenceTime&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;UtcNow&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;periodStart&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;referenceTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSeconds&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;numberOfEvents&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;eventsContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&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="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserIdentifier&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;userIdentifier&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&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;ActionIdentifier&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;actionIdentifier&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&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;When&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;periodStart&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Rate limited - respond 429 status code&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;numberOfEvents&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;http&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;StatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;429&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="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Not rate limited&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&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 should be it, right? &lt;em&gt;RIGHT?!?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Well… Let’s start with the good. This would be very flexible in defining various limits and combinations of limits. It’s just code, and the logic is up to you!&lt;/p&gt;

&lt;p&gt;However, every request is at least 2 queries to handle potential rate limiting. The &lt;code&gt;Events&lt;/code&gt; table will grow. And fast! So you will need to remove events at some point.&lt;/p&gt;

&lt;p&gt;The database server will suffer at scale. Imposing rate limits to protect shared resources, has now increased the load on this shared resource!&lt;/p&gt;

&lt;p&gt;Ideally, the measurements and logic for your rate limiting solution should not add this additional load. A simple counter per user identifier and action identifier should be sufficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate limiting algorithms
&lt;/h2&gt;

&lt;p&gt;Luckily for us, smart people have thought long and hard about the topic of rate limiting, and came up with a number of rate limiting algorithms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quantized buckets / Fixed window limit
&lt;/h3&gt;

&lt;p&gt;An easy algorithm for rate limiting, is using quantized buckets, also known as fixed window limits. In short, the idea is that you keep a counter for a specific time window, and apply limits based on that.&lt;/p&gt;

&lt;p&gt;An example would be to allow “100 requests per minute” to a given resource. Using a simple function, you can get the same identifier for a specific period of time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetBucketName&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;operation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt; &lt;span class="n"&gt;timespan&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;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Floor&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;UtcNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ticks&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;timespan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalMilliseconds&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;operation&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;bucket&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="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="nf"&gt;GetBucketName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"someaction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="c1"&gt;// someaction_106062120 &amp;lt;-- this will be the key for +/- 10 minutes&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could keep the generated bucket name + counter in a dictionary, and increment the counter for every request. based on the counter, you can then apply the rate limit. When a new time window begins, a new bucket name is generated and the counter can start from 0.&lt;/p&gt;

&lt;p&gt;This bucket name + counter can be stored in a C# dictionary, or as a named value on Redis that you can easily increment (and expires after a specific time so Redis does the housekeeping for you).&lt;/p&gt;

&lt;p&gt;There is a drawback to quantized buckets / fixed window limits… They are not entirely accurate.&lt;/p&gt;

&lt;p&gt;Let’s say you want to allow “4 requests per 10 seconds”. Per 10-second window, you allow only 4 requests. If all of those requests come in at the end of the previous window and the start of the current window, there’s a good chance the expected limit is going to be exceeded.&lt;/p&gt;

&lt;p&gt;The limit of 4 requests is true per fixed window, but not per sliding window…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sQrepNpW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/09/fixed-window-rate-limiting.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sQrepNpW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/09/fixed-window-rate-limiting.png" alt="Fixed window limit / Quantized bucket requests may exceed expected limit" width="880" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Does this matter? As always, “it depends”.&lt;/p&gt;

&lt;p&gt;If you want to really lock things down and don’t want to tolerate a potential overrun, then yes, this matters. If your goal is to impose rate limits to prevent accidental or intentional excessive resource usage, perhaps this potential overrun does not matter.&lt;/p&gt;

&lt;p&gt;In the case where you do need a sliding window limit, you could look into &lt;em&gt;sliding window limit&lt;/em&gt; approaches. These usually combine multiple smaller fixed windows under the hood, to reduce the chance of overrunning the imposed limits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Token buckets
&lt;/h3&gt;

&lt;p&gt;Widely used in telecommunications to deal with bandwidth usage and bandwidth bursts, are token buckets. Token buckets control flow rate, and they are called &lt;em&gt;buckets&lt;/em&gt; because buckets and water are a great analogy!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Imagine a bucket where water is poured in at the top and leaks from the bottom. If the rate at which water is poured in exceeds the rate at which it leaks water out, the bucket overflows and no new requests can be handled until there’s capacity in the bucket again.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you don’t like water, you could use tokens instead:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Imagine you have a bucket that’s completely filled with tokens. When a request comes in, you take a token out of the bucket. After a predetermined amount of time, new tokens are added to the bucket. If you take tokens out faster than they are added, the bucket will be empty at some point, and no new requests can be handled until new tokens are added.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In code, this could look like the following. The &lt;code&gt;GetCallsLeft()&lt;/code&gt; method returns how many tokens are left in the bucket.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;GetCallsLeft&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;_tokens&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;_capacity&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;referenceTime&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;UtcNow&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;delta&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;referenceTime&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;_lastRefill&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Ticks&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;_interval&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ticks&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;delta&lt;/span&gt; &lt;span class="p"&gt;&amp;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="n"&gt;_tokens&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_tokens&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;_capacity&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="n"&gt;_lastRefill&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;referenceTime&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;return&lt;/span&gt; &lt;span class="n"&gt;_tokens&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;One benefit of token buckets is that they don’t suffer the issue we saw with quantized buckets. If too many requests come in, the bucket overflows (or is empty if you prefer the water analogy) and requests are limited.&lt;/p&gt;

&lt;p&gt;Another benefit is that they allow bursts in traffic: if your bucket allows for 60 tokens per minute (replenished every second), clients can still burst up to 60 requests for the duration of 1 second, and thereafter the flow rate becomes 1 request per second (because of this replenishment flow).&lt;/p&gt;

&lt;p&gt;There are other variations of the algorithms we have seen, but generally speaking they will correspond to either quantized buckets or token buckets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deciding rate limits
&lt;/h2&gt;

&lt;p&gt;Now that we have seen the basic concepts of rate limiting, let’s have a look at the decisions to be made before implementing rate limiting in your applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which resources to rate limit?
&lt;/h3&gt;

&lt;p&gt;Deciding which resources to rate limit is easy. Here’s a quote from a famous blog author:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Rate limit everything.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;– Maarten Balliauw&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your application typically employs a “time-sharing model”. Much like a time-sharing vacation property, you don’t want your guests to be hindered by other guests, and ideally come up with a fair model that allows everyone to use the vacation property in a fair way.&lt;/p&gt;

&lt;p&gt;Rate limiting should be applied to every endpoint that uses resources that could slow down or break your application when exhausted or stressed. Given every request uses at least the CPU and memory of your server, and potentially also disk I/O, the database, external APIs and more, you’ll want to apply rate limiting to every endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are sensible limits?
&lt;/h3&gt;

&lt;p&gt;Deciding on sensible limits is hard, and the only good answer here is to measure what typical usage looks like.&lt;/p&gt;

&lt;p&gt;Measurement brings knowledge! A good approach to decide on sensible limits is to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find out the current # of requests for a certain resource in your application.&lt;/li&gt;
&lt;li&gt;Implement rate limiting, but don’t block requests yet. When a limit is hit, log it. This will let you fine-tune the numbers.&lt;/li&gt;
&lt;li&gt;Iterate on measurements and logs, and when you are certain you know what the limit should be, start enforcing it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As an extra tip, make sure to constantly monitor rate limiting events, and adjust when needed. Perhaps a newer version of your mobile app makes more requests to your API, and this is expected traffic.&lt;/p&gt;

&lt;p&gt;Too strict limits will annoy your users. Remember, you don’t want to police the number of requests. You want fair usage of resources. You don’t call the police when two toddlers fight over a toy. If they both need the toy, maybe it’s fine to have multiple toys or have them play at different times, so they don’t have to fight over it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Will you allow bursts or not?
&lt;/h3&gt;

&lt;p&gt;Depending on your application and endpoint, having one rate limit in place will be enough. For example, a global rate limit of 600 requests per minute may be perfect for every endpoint in your application.&lt;/p&gt;

&lt;p&gt;However, sometimes you may want to allow bursts. For example, when your mobile app starts, it performs some initial requests in rapid succession to get the latest data from your API, and after that it slows down.&lt;/p&gt;

&lt;p&gt;To handle these bursts, you may want to implement a “laddering” approach, and have multiple different limits in place:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Limit&lt;/th&gt;
&lt;th&gt;Operation A&lt;/th&gt;
&lt;th&gt;Operation B&lt;/th&gt;
&lt;th&gt;Operation C&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Per second&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per minute&lt;/td&gt;
&lt;td&gt;60&lt;/td&gt;
&lt;td&gt;60&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per hour&lt;/td&gt;
&lt;td&gt;3600&lt;/td&gt;
&lt;td&gt;600&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In the above table, a client could make 10 requests per second to &lt;em&gt;Operation A&lt;/em&gt;. 10 per second would normally translate to 36000 request per hour, but maybe at the hourly level, only 3600 is a better number.&lt;/p&gt;

&lt;p&gt;Again, measure, and don’t prematurely add laddering. There’s a good chance a single limit for all endpoints in your application may be sufficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  What will be the partition/identifier?
&lt;/h3&gt;

&lt;p&gt;Previously, we used a &lt;em&gt;user identifier + action/operation identifier&lt;/em&gt; to impose rate limits. There are many other request properties you can use to partition your requests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Partition per endpoint&lt;/li&gt;
&lt;li&gt;Partition per IP address 

&lt;ul&gt;
&lt;li&gt;Keep in mind users may be sharing an IP address, e.g. when behind a NAT/CGNAT/proxy&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Partition per user 

&lt;ul&gt;
&lt;li&gt;Keep in mind you may have anonymous users, how will you distinguish those?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Partition per session 

&lt;ul&gt;
&lt;li&gt;What if your user starts a new session for every request?&lt;/li&gt;
&lt;li&gt;What if your user makes use of multiple devices with separate sessions?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Partition per browser&lt;/li&gt;
&lt;li&gt;Partition per header (e.g. &lt;code&gt;X-Api-Token&lt;/code&gt;), …&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also here, “it depends” on your application. A global rate limit per IP address may work for your application. More complex applications may need a combination of these, e.g. per-endpoint rate limiting combined with the current user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decide on exceptions
&lt;/h3&gt;

&lt;p&gt;Should rate limiting apply to all requests? Well… yes! We already discussed all endpoints in your application should be rate limited.&lt;/p&gt;

&lt;p&gt;A better question would be whether the same limits should apply for all types of users. As usual, the answer to this question will depend on your application. There is no silver bullet, but here are some examples to think about.&lt;/p&gt;

&lt;p&gt;Good candidates to have different rate limits in place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your automated monitoring - the last thing you want is nightly PagerDuty alerts because of your monitoring system being rate limited. 

&lt;ul&gt;
&lt;li&gt;Counter point: maybe you do want to have a rate limit in place, so your monitoring can check rate limits are enabled?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Your admin/support team - your support team may make a lot of requests to your application to help out users, so it’s best to not get in their way. 

&lt;ul&gt;
&lt;li&gt;Counter point: maybe a rate limit does make sense, so a disgruntled employee can’t go and scrape lots of data or add swear words into lots of places with an automated script.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Web crawlers - your marketing folks won’t be happy if your app is not visible in search engines! 

&lt;ul&gt;
&lt;li&gt;Counter point: there are aggressive crawlers, and you also don’t want them to get in the way of your users. There are some &lt;code&gt;robots.txt&lt;/code&gt; entries many spiders respect, but a rate limit could be needed.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;“Datacenter IP ranges” - If you have a mobile app targeted at consumers, does traffic coming from AWS, Azure and other big hosters make sense? If you expect mostly “residential” and mobile traffic, perhaps you can reduce automated traffic from other sources with a more strict rate limit. There’s a list of &lt;a href="https://github.com/client9/ipcat"&gt;datacenter IP ranges&lt;/a&gt; that you can use for this. 

&lt;ul&gt;
&lt;li&gt;Counter point: VPN providers out there are often going to be in these IP ranges. Legitimate users may use datacenter IP addresses in those cases. Also at the time of writing, my dad’s Starlink subscription runs over what looks like a Google Compute Engine IP address.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;An additional exception could be certain groups of customers. If your API is your product, it could be part of your business model to allow e.g. users of your “premium plan” to have different limits.&lt;/p&gt;

&lt;p&gt;Also here, measuring will help you make an informed decision. If you see excess traffic from web crawlers, a tighter rate limit may be needed. If you see your support folks unable to help users, maybe a less strict rate limit for them makes more sense.&lt;/p&gt;

&lt;h3&gt;
  
  
  Responding to limits
&lt;/h3&gt;

&lt;p&gt;What should happen when a request is being rate limited? You could “black hole” the request and silently abort it, but it’s much nicer to communicate what is happening, and why.&lt;/p&gt;

&lt;p&gt;One example I like is &lt;a href="https://www.stackoverflow.com/"&gt;StackOverflow&lt;/a&gt;. When using their website and posting responses to many questions in rapid succession, there’s a good chance their rate limiter may ask you to prove you are human:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tyBVOKLC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/09/stackoverflow-rate-limiting.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tyBVOKLC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/09/stackoverflow-rate-limiting.png" alt="Rate limiting at StackOverflow asks you to prove you are human" width="779" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is pretty slick. Potential issues with a broken application posting multiple answers rapidly are avoided by rate limiting. Potential scripts and bots will also be rate limited, and their service happily hums along.&lt;/p&gt;

&lt;p&gt;Another good example is GitHub. First of all, &lt;a href="https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting"&gt;they document their rate limits&lt;/a&gt; so that you can account for these limits in any app you may be building that uses their API. Second, any request you make will get a response with information about how many requests are remaining, and when more will be available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -I https://api.github.com/users/octocat
&amp;gt; HTTP/2 200
&amp;gt; Date: Mon, 01 Jul 2013 17:27:06 GMT
&amp;gt; x-ratelimit-limit: 60
&amp;gt; x-ratelimit-remaining: 56
&amp;gt; x-ratelimit-used: 4
&amp;gt; x-ratelimit-reset: 1372700873

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition, when a rate limit is exceeded, you’ll get a response that says what happened and why, and where to find more information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; HTTP/2 403
&amp;gt; Date: Tue, 20 Aug 2013 14:50:41 GMT
&amp;gt; x-ratelimit-limit: 60
&amp;gt; x-ratelimit-remaining: 0
&amp;gt; x-ratelimit-used: 60
&amp;gt; x-ratelimit-reset: 1377013266

&amp;gt; {
&amp;gt; "message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)",
&amp;gt; "documentation_url": "https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting"
&amp;gt; }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have mixed types of users, you could inspect the &lt;code&gt;Accept&lt;/code&gt; header and return different responses based on whether &lt;code&gt;text/html&lt;/code&gt; is requested (likely a browser) and when &lt;code&gt;application/json&lt;/code&gt; is requested (likely an API client).&lt;/p&gt;

&lt;p&gt;Other services have documented their limits as well. For example, &lt;a href="https://learn.microsoft.com/en-us/nuget/api/rate-limits"&gt;NuGet lists limits for each endpoint&lt;/a&gt; and also shows you what the response would look like when a limit is reached.&lt;/p&gt;

&lt;p&gt;Try and always communicate why a client is being limited, and when to retry. A link to the documentation may be enough.&lt;/p&gt;

&lt;p&gt;This is of course not mandatory, but if you’re offering an API to your users, it does help in providing a great developer experience.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;429, 403, 503, … status codes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From the GitHub example, you may have seen the status code returned when rate limits are exceeded is &lt;code&gt;403&lt;/code&gt; (&lt;a href="https://httpstatusdogs.com/403-forbidden"&gt;Forbidden&lt;/a&gt;). Other services return a &lt;code&gt;503&lt;/code&gt; (&lt;a href="https://httpstatusdogs.com/503-service-unavailable"&gt;Service unavailable&lt;/a&gt;), and others return a &lt;code&gt;429&lt;/code&gt; status code (&lt;a href="https://httpstatusdogs.com/429-too-many-requests"&gt;Too Many Requests&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;There’s no strict rule here, but it does look like many services out there follow a convention of using &lt;code&gt;429 Too Many Requests&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Where to store rate limit data and counters?
&lt;/h3&gt;

&lt;p&gt;When you search for information about rate limiting, there’s a good chance you’ll come across questions about where to store rate limit data and counters. More than once, you’ll see questions related to using your database, Redis or other distributed cached.&lt;/p&gt;

&lt;p&gt;Keep it simple. Do you &lt;em&gt;really&lt;/em&gt; need 100% accurate counters that all instances of your application share? Or is it enough to apply “10-ish requests per second per user” on every instance of your application and be done with it?&lt;/p&gt;

&lt;p&gt;If your rate limit is part of your revenue model, for example when you sell API access with specific resource guarantees, then you’ll probably want to look into shared and accurate counters. When your goal is to ensure fair use of shared resources in your application, storing counters per instance may be more than enough.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where to apply rate limiting?
&lt;/h3&gt;

&lt;p&gt;In an ideal world, the consumer of your application would know about rate limits and apply them there, before even attempting a request. This would mean your server and application will never even have to process the request.&lt;/p&gt;

&lt;p&gt;Unfortunately, we’re not living in an ideal world, and clients will send requests to your application. How far will you let traffic flow?&lt;/p&gt;

&lt;p&gt;If you think of web-based applications (including APIs and the likes), there are several places where rate limits could be applied.&lt;/p&gt;

&lt;p&gt;Maybe you are using a Content Delivery Network (CDN) that acts as a reverse proxy for your application, and they can rate limit? Or perhaps the framework you are using has some rate limiting infrastructure that can be used?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P1VaTIHb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/09/rate-limiting-where.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P1VaTIHb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/09/rate-limiting-where.png" alt="Where to rate limit?" width="828" height="743"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The closer to your application you add rate limiting, the more knowledge you will have about the user. If your partitioning requires deep knowledge about user privileges etc., your application may be the only place where rate limiting can be applied.&lt;/p&gt;

&lt;p&gt;When you partition based on IP address and the &lt;code&gt;Authentication&lt;/code&gt; header, a CDN or reverse proxy could handle rate limiting as they don’t need extra data for every request.&lt;/p&gt;

&lt;p&gt;The closer to your application you add rate limiting, the more resources will be spent. If you’re running a serverless application and rate limit on a CDN or reverse proxy, you won’t be billed for execution of your serverless function. If you need more information about the user, then your serverless function may need to apply rate limiting (but also costs money).&lt;/p&gt;

&lt;p&gt;Depending on what makes sense for your application, here are some resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In your application 

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/stefanprodan/AspNetCoreRateLimit"&gt;Stefan Prodan’s AspNetCoreRateLimit&lt;/a&gt; (highly recommend, it has lots of options for every aspect discussed in this blog post)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/maartenba/aspnet-core-rate-limiting-middleware-in-net-7-5ec1"&gt;ASP.NET Core Rate Limiting middleware in .NET 7&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Reverse proxy 

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.nginx.com/nginx/admin-guide/security-controls/controlling-access-proxied-http/"&gt;NGinx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.haproxy.com/blog/four-examples-of-haproxy-rate-limiting/"&gt;HAProxy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://doc.traefik.io/traefik/middlewares/http/ratelimit/"&gt;Traefik&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/maartenba/aspnet-core-rate-limiting-middleware-in-net-7-5ec1"&gt;YARP reverse proxy + ASP.NET Core Rate Limiting middleware&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Content Delivery Network / API Gateway 

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/api-management/api-management-sample-flexible-throttling"&gt;Azure API Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/web-application-firewall/afds/waf-front-door-rate-limit"&gt;Azure Front Door&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.cloudflare.com/hc/en-us/articles/115001635128-Configuring-Cloudflare-Rate-Limiting"&gt;CloudFlare&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html"&gt;Amazon API Gateway&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h3&gt;
  
  
  Monitoring and circuit breakers
&lt;/h3&gt;

&lt;p&gt;Applications change, usage patterns change, and as such, rate limits will also need to change. Perhaps your rules are too strict and hurting your users more than your application resources. Perhaps the latest deployment introduced a bug that is making excess calls to an API, and this needs to be fixed?&lt;/p&gt;

&lt;p&gt;Keep an eye on your rate limiting, keep track of who gets rate limited, when and why. Use custom metrics to build dashboards on # of rate limiting actions kicking in to help during incident troubleshooting.&lt;/p&gt;

&lt;p&gt;Also make sure you can adapt quickly if needed, by having circuit breakers in place. If with a new deployment all of your users experience rate limiting for some reason, having an emergency switch to just turn off rate limits will be welcome. Perhaps on/off is too coarse, and your circuit breaker could be in making rate limits dynamic and allowing for updates using a configuration file.&lt;/p&gt;

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

&lt;p&gt;Whether intentional or accidental, users of your application will bring along unexpected usage patterns. Excess requests, request bursts, automated scripts, brute-force requests - all of these are going to happen at some point.&lt;/p&gt;

&lt;p&gt;These types of usage may pose a potential threat to your application’s health, and one abusive user could impact several others. Your application runs on shared resources, and ideally you want them to be shared in a fair manner.&lt;/p&gt;

&lt;p&gt;This is where rate limiting comes in, and I hope I was able to give you a comprehensive overview of all the things you can and have to consider when implementing a rate limiting solution.&lt;/p&gt;

&lt;p&gt;The concept of “it depends” definitely applies when building a rate limiting solution. Small and simple may be enough, and many of the considerations in this post will only apply for larger applications. But do consider to “rate limit everything” to make resource sharing more fair.&lt;/p&gt;

</description>
      <category>general</category>
      <category>web</category>
      <category>ratelimiting</category>
    </item>
    <item>
      <title>ASP.NET Core rate limiting middleware in .NET 7</title>
      <dc:creator>Maarten Balliauw</dc:creator>
      <pubDate>Mon, 26 Sep 2022 02:44:05 +0000</pubDate>
      <link>https://dev.to/maartenba/aspnet-core-rate-limiting-middleware-in-net-7-5ec1</link>
      <guid>https://dev.to/maartenba/aspnet-core-rate-limiting-middleware-in-net-7-5ec1</guid>
      <description>&lt;p&gt;Rate limiting is a way to control the amount of traffic that a web application or API receives, by limiting the number of requests that can be made in a given period of time. This can help to improve the performance of the site or application, and to prevent it from becoming unresponsive.&lt;/p&gt;

&lt;p&gt;Starting with .NET 7, ASP.NET Core includes a built-in rate limiting middleware, which can be used to rate limit web applications and APIs. In this blog post, we’ll take a look at how to configure and use the rate limiting middleware in ASP.NET Core.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is rate limiting?
&lt;/h2&gt;

&lt;p&gt;Every application you build is sharing resources. The application runs on a server that shares its CPU, memory, and disk I/O, on a database that stores data for all your users.&lt;/p&gt;

&lt;p&gt;Whether accidental or intentional, users may exhaust those resources in a way that impacts others. A script can make too many requests, or a new deployment of your mobile app has a regression that calls a specific API too many times and results in the database being slow. Ideally, all of your users get access to an equal amount of shared resources, within the boundary of what your application can support.&lt;/p&gt;

&lt;p&gt;Let’s say the database used by your application can safely handle around 1000 queries per minute. In your application, you can set a limit to only allow 1000 requests per minute to prevent the database from getting more requests.&lt;/p&gt;

&lt;p&gt;Instead of one global “1000 requests per minute” limit, you could look at your average application usage, and for example set a limit of “100 requests per user per minute”. Or chain those limits, and say “100 requests per user per minute, and 1000 requests per minute”.&lt;/p&gt;

&lt;p&gt;Rate limits will help to prevent the server from being overwhelmed by too many requests, and still makes sure that all users have a fair chance of getting their requests processed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate limiting in ASP.NET Core
&lt;/h2&gt;

&lt;p&gt;If your application is using .NET 7 (or higher), a rate limiting middleware is available out of the box. It provides a way to apply rate limiting to your web application and API endpoints.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Under the hood, the ASP.NET Core rate limiting middleware uses the &lt;a href="https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/"&gt;&lt;code&gt;System.Threading.RateLimiting&lt;/code&gt;&lt;/a&gt; subsystem. If you’re interested in rate limiting other resources, for example an &lt;code&gt;HttpClient&lt;/code&gt; making requests, or access to other resources, check it out!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Much like other middlewares, to enable the ASP.NET Core rate limiting middleware, you will have to add the required services to the service collection, and then enable the middleware for all request pipelines.&lt;/p&gt;

&lt;p&gt;Let’s add a simple rate limiter that limits all to 10 requests per minute, per authenticated username (or hostname if not authenticated):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRateLimiter&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;=&amp;gt;&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="n"&gt;GlobalLimiter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PartitionedRateLimiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpContext&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;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;httpContext&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;RateLimitPartition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFixedWindowLimiter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;partitionKey&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;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;httpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Host&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;factory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;partition&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;FixedWindowRateLimiterOptions&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;AutoReplenishment&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="n"&gt;PermitLimit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;QueueLimit&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;Window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&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;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&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;UseRateLimiter&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;MapGet&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;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Too much at once? I agree, so let’s try to break it down.&lt;/p&gt;

&lt;p&gt;The call to &lt;code&gt;builder.Services.AddRateLimiter(...)&lt;/code&gt; registers the ASP.NET Core middleware with the service collection, including its configuration options. There are many &lt;code&gt;options&lt;/code&gt; that can be specified, such as the HTTP status code being returned, what should happen when rate limiting applies, and additional policies.&lt;/p&gt;

&lt;p&gt;For now, let’s just assume we want to have one global rate limiter for all requests. The &lt;code&gt;GlobalLimiter&lt;/code&gt; option can be set to any &lt;code&gt;PartitionedRateLimiter&lt;/code&gt;. In this example, we’re adding a &lt;code&gt;FixedWindowLimiter&lt;/code&gt;, and configure it to apply “per authenticated username (or hostname if not authenticated)” - the &lt;code&gt;partition&lt;/code&gt;. The &lt;code&gt;FixedWindowLimiter&lt;/code&gt; is then configured to automatically replenish permitted requests, and permits “10 requests per minute”.&lt;/p&gt;

&lt;p&gt;Further down the code, you’ll see a call to &lt;code&gt;app.UseRateLimiter()&lt;/code&gt;. This enables the rate limiting middleware using the options specified earlier.&lt;/p&gt;

&lt;p&gt;If you run the application and refresh quickly, you’ll see at some point a &lt;code&gt;503 Service Unavailable&lt;/code&gt; is returned, which is when the rate limiting middleware does its thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure what happens when being rate limited
&lt;/h2&gt;

&lt;p&gt;Not happy with that &lt;code&gt;503&lt;/code&gt; being returned when rate limiting is enforced? Let’s look at how to configure that!&lt;/p&gt;

&lt;p&gt;Many services settled on the &lt;code&gt;429 Too Many Requests&lt;/code&gt; status code. In order to change the status code, you can set the &lt;code&gt;RejectionStatusCode&lt;/code&gt; option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRateLimiter&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;=&amp;gt;&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="n"&gt;RejectionStatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;429&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, there’s an &lt;code&gt;OnRejected&lt;/code&gt; option you can set to customize the response that is sent when rate limiting is triggered for a request. It’s a good practice to communicate what happened, and why a rate limit applies. So instead of going with the default of returning “just a status code”, you can return some more meaningful information. The &lt;code&gt;OnRejected&lt;/code&gt; delegate gives you access to the current rate limit context, including the &lt;code&gt;HttpContext&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here’s an example that sets the response status code to &lt;code&gt;429&lt;/code&gt;, and returns a meaningful response. The response mentions when to retry (if available from the rate limiting metadata), and provides a documentation link where users can find out more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRateLimiter&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;=&amp;gt;&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="n"&gt;OnRejected&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&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;token&lt;/span&gt;&lt;span class="p"&gt;)&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;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;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;429&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lease&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MetadataName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RetryAfter&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;retryAfter&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;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;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="s"&gt;$"Too many requests. Please try again after &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;retryAfter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalMinutes&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; minute(s). "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                &lt;span class="s"&gt;$"Read more about our rate limits at https://example.org/docs/ratelimiting."&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="n"&gt;token&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="k"&gt;await&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;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="s"&gt;"Too many requests. Please try again later. "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                &lt;span class="s"&gt;"Read more about our rate limits at https://example.org/docs/ratelimiting."&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="n"&gt;token&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="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Given you have access to the current &lt;code&gt;HttpContext&lt;/code&gt;, you also have access to the service collection. It’s a good practice to keep an eye on who, when and why a rate limit is being enforced, and you could log that by grabbing an &lt;code&gt;ILogger&lt;/code&gt; from &lt;code&gt;context.HttpContext.RequestServices&lt;/code&gt; if needed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Be careful with the logic you write in your &lt;code&gt;OnRejected&lt;/code&gt; implementation. If you use your database context and run 5 queries, your rate limit isn’t actually helping reduce strain on your database. Communicate with the user and return a meaningful error (you could even use the &lt;code&gt;Accept&lt;/code&gt; header and return either JSON or HTML depending on the client type), but don’t consume more resources than a normal response would require.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Speaking of communicating about what and why, the ASP.NET Core rate limiting middleware is a bit limited (pun not intended). The metadata you have access to is sparse (“retry after” is pretty much the only useful metadata returned).&lt;/p&gt;

&lt;p&gt;Additionally, if you would want to return statistics about your limits (e.g. &lt;a href="https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limit-http-headers"&gt;like GitHub does&lt;/a&gt;), you’ll find the ASP.NET Core rate limiting middleware does not support this. You won’t have access to the “number of requests remaining” or other metadata. Not in &lt;code&gt;OnRejected&lt;/code&gt;, and definitely not if you want to return this data as headers on every request.&lt;/p&gt;

&lt;p&gt;If this is something that matters to you, I advise to check out &lt;a href="https://github.com/stefanprodan/AspNetCoreRateLimit"&gt;Stefan Prodan’s &lt;code&gt;AspNetCoreRateLimit&lt;/code&gt;&lt;/a&gt;, which has many (many!) more options available. Or &lt;a href="https://github.com/dotnet/aspnetcore/issues/44140"&gt;chime in on this GitHub issue&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of rate limiters
&lt;/h2&gt;

&lt;p&gt;In our example, we’ve used the &lt;code&gt;FixedWindowLimiter&lt;/code&gt; to limit the number of requests in a time window.&lt;/p&gt;

&lt;p&gt;There are more rate limiting algorithms available in .NET that you can use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/#concurrency-limit"&gt;&lt;strong&gt;Concurrency limit&lt;/strong&gt;&lt;/a&gt; is the simplest form of rate limiting. It doesn’t look at time, just at number of concurrent requests. “Allow 10 concurrent requests”.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/#fixed-window-limit"&gt;&lt;strong&gt;Fixed window limit&lt;/strong&gt;&lt;/a&gt; lets you apply limits such as “60 requests per minute”. Every minute, 60 requests can be made. One every second, but also 60 in one go.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/#sliding-window-limit"&gt;&lt;strong&gt;Sliding window limit&lt;/strong&gt;&lt;/a&gt; is similar to the fixed window limit, but uses segments for more fine-grained limits. Think “60 requests per minute, with 1 request per second”.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/#token-bucket-limit"&gt;&lt;strong&gt;Token bucket limit&lt;/strong&gt;&lt;/a&gt; lets you control flow rate, and allows for bursts. Think “you are given 100 requests every minute”. If you make all of them over 10 seconds, you’ll have to wait for 1 minute before you are allowed more requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition, you can “chain” rate limiters of one type of various types, using the &lt;code&gt;PartitionedRateLimiter.CreateChained()&lt;/code&gt; helper.&lt;/p&gt;

&lt;p&gt;Maybe you want to have a limit where one can make 600 requests per minute, but only 6000 per hour. You could chain two &lt;code&gt;FixedWindowLimiter&lt;/code&gt; with different options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRateLimiter&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;=&amp;gt;&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="n"&gt;GlobalLimiter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PartitionedRateLimiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateChained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;PartitionedRateLimiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpContext&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;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;httpContext&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;RateLimitPartition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFixedWindowLimiter&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="nf"&gt;ResolveClientIpAddress&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;partition&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;FixedWindowRateLimiterOptions&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;AutoReplenishment&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="n"&gt;PermitLimit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&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="n"&gt;PartitionedRateLimiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpContext&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;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;httpContext&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;RateLimitPartition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFixedWindowLimiter&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="nf"&gt;ResolveClientIpAddress&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;partition&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;FixedWindowRateLimiterOptions&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;AutoReplenishment&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="n"&gt;PermitLimit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;6000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromHours&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="c1"&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 that the &lt;code&gt;ResolveClientIpAddress()&lt;/code&gt; extension method I use here is just an example that checks different headers for the current client’s IP address. Use a partition key that makes sense for your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Queue requests instead of rejecting them: &lt;code&gt;QueueLimit&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;On most of the rate limiters that ship with .NET, you can specify a &lt;code&gt;QueueLimit&lt;/code&gt; next to the &lt;code&gt;PermitLimit&lt;/code&gt;. The &lt;code&gt;QueueLimit&lt;/code&gt; specifies how many incoming requests will be queued but not rejected when the &lt;code&gt;PermitLimit&lt;/code&gt; is reached.&lt;/p&gt;

&lt;p&gt;Let’s look at an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;PartitionedRateLimiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpContext&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;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;httpContext&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;RateLimitPartition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFixedWindowLimiter&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="nf"&gt;ResolveClientIpAddress&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;partition&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;FixedWindowRateLimiterOptions&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;AutoReplenishment&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="n"&gt;PermitLimit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;QueueLimit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;QueueProcessingOrder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QueueProcessingOrder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OldestFirst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example, clients can make 10 requests per second. If they make more requests per second, up to 6 of those excess requests will be queued and will seemingly “hang” instead of being rejected. The next second, this queue will be processed.&lt;/p&gt;

&lt;p&gt;If you expect small traffic bursts, setting &lt;code&gt;QueueLimit&lt;/code&gt; may provide a nicer experience to your users. Instead of rejecting their requests, you’re delaying them a bit.&lt;/p&gt;

&lt;p&gt;I’d personally not go with large &lt;code&gt;QueueLimit&lt;/code&gt;, and definitely not for long time windows. As a consumer of an API, I’d rather get a response back fast. Even if it’s a failure, as those can be retried. A few seconds of being in a queue may make sense, but any longer the client will probably time out anyway and your queue is being kept around with no use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create custom rate limiting policies
&lt;/h3&gt;

&lt;p&gt;Next to the default rate limiters, you can build your own implementation of &lt;code&gt;IRateLimiterPolicy&amp;lt;TPartitionKey&amp;gt;&lt;/code&gt;. This interface specifies 2 methods: &lt;code&gt;GetPartition()&lt;/code&gt;, which you’ll use to create a specific rate limiter for the current &lt;code&gt;HttpContext&lt;/code&gt;, and &lt;code&gt;OnRejected()&lt;/code&gt; if you want to have a custom response when this policy is rejecting a request.&lt;/p&gt;

&lt;p&gt;Here’s an example where the rate limiter options are partitioned by either the current authenticated user, or their hostname. Authenticated users get higher limits, too:&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;ExampleRateLimiterPolicy&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRateLimiterPolicy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;RateLimitPartition&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetPartition&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;httpContext&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;httpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;IsAuthenticated&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="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;RateLimitPartition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFixedWindowLimiter&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;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;!,&lt;/span&gt;
                &lt;span class="n"&gt;partition&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;FixedWindowRateLimiterOptions&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;AutoReplenishment&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="n"&gt;PermitLimit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;RateLimitPartition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFixedWindowLimiter&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;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Host&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;partition&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;FixedWindowRateLimiterOptions&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;AutoReplenishment&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="n"&gt;PermitLimit&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;Window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&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;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OnRejectedContext&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="n"&gt;ValueTask&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;?&lt;/span&gt; &lt;span class="n"&gt;OnRejected&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="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;_&lt;/span&gt;&lt;span class="p"&gt;)&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;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;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;418&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// I'm a 🫖&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;ValueTask&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And instead of rejecting requests with a well-known status code, this policy rejects requests with a &lt;code&gt;418&lt;/code&gt; status code (“I’m a teapot”).&lt;/p&gt;

&lt;h2&gt;
  
  
  Policies for rate limiting groups of endpoints
&lt;/h2&gt;

&lt;p&gt;So far, we’ve covered global limits that apply to all requests. There’s a good chance you want to apply different limits to different groups of endpoints. You may have endpoints that you don’t want to rate limit at all.&lt;/p&gt;

&lt;p&gt;This is where policies come in. In your configuration options, you can create different policies using the &lt;code&gt;.Add{RateLimiter}()&lt;/code&gt; extension methods, and then apply them to specific endpoints or groups thereof.&lt;/p&gt;

&lt;p&gt;Here’s an example configuration adding 2 fixed window limiters with different settings, and a different policy name (&lt;code&gt;"Api"&lt;/code&gt; and &lt;code&gt;"Web"&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;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRateLimiter&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;=&amp;gt;&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="nf"&gt;AddFixedWindowLimiter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Api"&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;=&amp;gt;&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="n"&gt;AutoReplenishment&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="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PermitLimit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&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="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFixedWindowLimiter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Web"&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;=&amp;gt;&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="n"&gt;AutoReplenishment&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="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PermitLimit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&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="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we look at how to apply these policies, let’s first cover an important warning…&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; The &lt;code&gt;.Add{RateLimiter}()&lt;/code&gt; extension methods partition rate limits based on the policy name. This is okay if you want to apply global limits per group of endpoints, but it’s not when you want to partition per user or per IP address or something along those lines.&lt;/p&gt;

&lt;p&gt;If you want to add policies that are partitioned by policy name &lt;em&gt;and&lt;/em&gt; any aspect of an incoming HTTP request, use the &lt;code&gt;.AddPolicy(..)&lt;/code&gt; method instead:&lt;/p&gt;


&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Api"&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;=&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;RateLimitPartition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFixedWindowLimiter&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="nf"&gt;ResolveClientIpAddress&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="n"&gt;partition&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;FixedWindowRateLimiterOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;AutoReplenishment&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="n"&gt;PermitLimit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;Window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&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;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;With that out of the way, let’s see how you can apply policies to certain endpoints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rate limiting policies with ASP.NET Core Minimal API
&lt;/h3&gt;

&lt;p&gt;When using ASP.NET Core Minimal API, you can enable a specific policy per endpoint, or per group of endpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Endpoint&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;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;RequireRateLimiting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Api"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Group&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;MapGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/orders"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;RequireRateLimiting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Api"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, you can disable rate limiting per endpoint or group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Endpoint&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;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;DisableRateLimiting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Group&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;MapGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/orders"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;DisableRateLimiting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rate limiting policies with ASP.NET Core MVC
&lt;/h3&gt;

&lt;p&gt;When using ASP.NET Core MVC, you can enable and disable policies per controller or action.&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;EnableRateLimiting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Api"&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;Orders&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DisableRateLimiting&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;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Index&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="nf"&gt;View&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;EnableRateLimitingAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ApiListing"&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;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;List&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="nf"&gt;View&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’ll find this works similar to authorization and authorization policies.&lt;/p&gt;

&lt;h2&gt;
  
  
  ASP.NET Core rate limiting with YARP proxy
&lt;/h2&gt;

&lt;p&gt;In your application, you may be using &lt;a href="https://microsoft.github.io/reverse-proxy/"&gt;YARP&lt;/a&gt;, to build a reverse proxy gateway sitting in front of various backend applications. For example, you may run YARP to listen on &lt;code&gt;example.org&lt;/code&gt;, and have it proxy all requests going to this domain while mapping &lt;code&gt;/api&lt;/code&gt; and &lt;code&gt;/docs&lt;/code&gt; to different web apps running on diffreent servers.&lt;/p&gt;

&lt;p&gt;In such scenario, rate limiting will also be useful. You could rate limit each application separately, or apply rate limiting in the YARP proxy. Given both YARP and ASP.NET Core rate limiting are middlewares, they play well together.&lt;/p&gt;

&lt;p&gt;As an example, here’s a YARP proxy that applies a global rate limit of 10 requests per minute, partitioned by host header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Threading.RateLimiting&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;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRateLimiter&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;=&amp;gt;&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="n"&gt;RejectionStatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;429&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="n"&gt;GlobalLimiter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PartitionedRateLimiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpContext&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;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;httpContext&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;RateLimitPartition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFixedWindowLimiter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;partitionKey&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;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Host&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;factory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;partition&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;FixedWindowRateLimiterOptions&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;AutoReplenishment&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="n"&gt;PermitLimit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;QueueLimit&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;Window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&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;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddReverseProxy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromConfig&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;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ReverseProxy"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRateLimiter&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;MapReverseProxy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like with ASP.NET Core Minimal API and MVC apps, you can use the &lt;code&gt;AddRateLimiter()&lt;/code&gt; extension method to configure rate limits, and &lt;code&gt;AddReverseProxy()&lt;/code&gt; to register the YARP configuration.&lt;/p&gt;

&lt;p&gt;To then register the configured middlewares in your application, use the &lt;code&gt;UseRateLimiter()&lt;/code&gt; and &lt;code&gt;MapReverseProxy()&lt;/code&gt; can be used.&lt;/p&gt;

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

&lt;p&gt;By limiting the number of requests that can be made to your application, you can reduce the load on your server and have more fair usage of resources among your users. ASP.NET Core provides an easy way to implement rate limiting in your applications. By using the built-in middleware, you can easily configure rate limiting for your application.&lt;/p&gt;

&lt;p&gt;In this post, I wanted to give you some insights about how you can use the ASP.NET Core rate limiting middleware. It’s not as complete as &lt;a href="https://github.com/stefanprodan/AspNetCoreRateLimit"&gt;Stefan Prodan’s &lt;code&gt;AspNetCoreRateLimit&lt;/code&gt;&lt;/a&gt;, but there are enough options available to add rate limiting to your application.&lt;/p&gt;

&lt;p&gt;In a future blog post, I’ll cover more concepts around rate limiting. Stay tuned!&lt;/p&gt;

</description>
      <category>web</category>
      <category>aspnetcore</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>How to test ASP.NET Core Minimal APIs</title>
      <dc:creator>Maarten Balliauw</dc:creator>
      <pubDate>Sat, 04 Jun 2022 02:44:05 +0000</pubDate>
      <link>https://dev.to/maartenba/how-to-test-aspnet-core-minimal-apis-5enc</link>
      <guid>https://dev.to/maartenba/how-to-test-aspnet-core-minimal-apis-5enc</guid>
      <description>&lt;p&gt;How do you test that your &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis"&gt;ASP.NET Core Minimal API&lt;/a&gt; behaves as expected? Do you need to deploy your application? Can you write tests with frameworks like &lt;a href="https://xunit.net/"&gt;xUnit&lt;/a&gt;, &lt;a href="https://nunit.org/"&gt;NUnit&lt;/a&gt;, or &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-mstest"&gt;MSTest&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;In this post, you will learn the basics of testing ASP.NET Core Minimal APIs. You’ll get started with testing a “hello world” endpoint, and then test a more complex API that returns JSON data. You’ll finish with customizing the ASP.NET Core service collection, so you can customize services for your unit tests and integration tests.&lt;/p&gt;

&lt;p&gt;By the end of this post, you will have a good understanding of how to make sure your ASP.NET Core Minimal APIs behave as expected and can be deployed to production, even on Fridays!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This post was originally published on the &lt;a href="https://www.twilio.com/blog/"&gt;Twilio blog&lt;/a&gt; on June 06, 2022: &lt;a href="https://www.twilio.com/blog/test-aspnetcore-minimal-apis"&gt;How to test ASP.NET Core Minimal APIs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An OS that supports .NET (Windows/macOS/Linux)&lt;/li&gt;
&lt;li&gt;A .NET IDE (such as &lt;a href="https://www.jetbrains.com/rider/"&gt;JetBrains Rider&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;.NET 6.0 SDK or later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find &lt;a href="https://github.com/maartenba/TwilioTestingMinimalAPI"&gt;the source code for this tutorial on GitHub&lt;/a&gt;. Use it as a reference if you run into any issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a test project
&lt;/h2&gt;

&lt;p&gt;To get started, you will need to create a solution with two projects: an ASP.NET Core Minimal API that will contain the application, and a unit test project that will contain the tests. In this blog post, you will use &lt;a href="https://xunit.net/"&gt;xUnit&lt;/a&gt; as the testing framework.&lt;/p&gt;

&lt;p&gt;You can create this solution in your favorite .NET IDE, or using the .NET CLI. In the command line or terminal window, navigate to the folder you want your project to be created in, and run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new web &lt;span class="nt"&gt;-o&lt;/span&gt; MyMinimalApi
dotnet new xunit &lt;span class="nt"&gt;-o&lt;/span&gt; MyMinimalApi.Tests
dotnet add MyMinimalApi.Tests reference MyMinimalApi
dotnet new sln
dotnet sln add MyMinimalApi
dotnet sln add MyMinimalApi.Tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You now have a &lt;em&gt;MyMinimalApi.sln&lt;/em&gt; file, and two projects (&lt;em&gt;MyMinimalApi.csproj&lt;/em&gt; for the ASP.NET Core Minimal API, and &lt;em&gt;MyMinimalApi.Tests.csproj&lt;/em&gt; for the unit tests) with some template code. The test project also has a project reference to the Minimal API project.&lt;/p&gt;

&lt;p&gt;To run the Minimal API application, you can use the .NET CLI and specify the project to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run &lt;span class="nt"&gt;--project&lt;/span&gt; MyMinimalApi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tests can be run using the following .NET CLI command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s not a lot of useful code in these projects yet. The Minimal API project contains a &lt;em&gt;Program.cs&lt;/em&gt; file with an endpoint that returns the string “Hello World!”:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&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;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The test project (&lt;em&gt;MyMinimalApi.Tests.csproj&lt;/em&gt;) contains a template unit test file &lt;em&gt;UnitTest1.cs&lt;/em&gt; that you will replace later in this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update the test project
&lt;/h2&gt;

&lt;p&gt;Before you can start testing your Minimal API, you will need to make some updates to the test project. The unit tests need to be able to use the ASP.NET Core framework, so you’ll have to bring that in somehow. The easiest way to do this is by adding a reference to the &lt;code&gt;Microsoft.AspNetCore.Mvc.Testing&lt;/code&gt; package. This package also comes with several helper classes that are invaluable when writing unit tests later on.&lt;/p&gt;

&lt;p&gt;Add this package using your favorite IDE, or use the .NET CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add MyMinimalApi.Tests package Microsoft.AspNetCore.Mvc.Testing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;MyMinimalApi.Tests.csproj&lt;/em&gt; file now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="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;net6.0&lt;span class="nt"&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ImplicitUsings&amp;gt;&lt;/span&gt;enable&lt;span class="nt"&gt;&amp;lt;/ImplicitUsings&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class="nt"&gt;&amp;lt;/Nullable&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;IsPackable&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/IsPackable&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;"Microsoft.AspNetCore.Mvc.Testing"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"6.0.0"&lt;/span&gt; &lt;span class="nt"&gt;/&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;"Microsoft.NET.Test.Sdk"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"17.1.0"&lt;/span&gt; &lt;span class="nt"&gt;/&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;"xunit"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"2.4.1"&lt;/span&gt; &lt;span class="nt"&gt;/&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;"xunit.runner.visualstudio"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"2.4.3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;IncludeAssets&amp;gt;&lt;/span&gt;runtime; build; native; contentfiles; analyzers; buildtransitive&lt;span class="nt"&gt;&amp;lt;/IncludeAssets&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;PrivateAssets&amp;gt;&lt;/span&gt;all&lt;span class="nt"&gt;&amp;lt;/PrivateAssets&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/PackageReference&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;"coverlet.collector"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"3.1.2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;IncludeAssets&amp;gt;&lt;/span&gt;runtime; build; native; contentfiles; analyzers; buildtransitive&lt;span class="nt"&gt;&amp;lt;/IncludeAssets&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;PrivateAssets&amp;gt;&lt;/span&gt;all&lt;span class="nt"&gt;&amp;lt;/PrivateAssets&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/PackageReference&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;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ProjectReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"..\MinimalAPI\MinimalAPI.csproj"&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;You can now start writing unit tests for your Minimal API.&lt;/p&gt;

&lt;h2&gt;
  
  
  “Hello World” and the ASP.NET Core test server
&lt;/h2&gt;

&lt;p&gt;In the Minimal API project, &lt;em&gt;Program.cs&lt;/em&gt; already defines a “Hello World!” endpoint. You will test this endpoint first. Before you can do this, you will need to add the following public partial class definition at the bottom of &lt;em&gt;Program.cs&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&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 reason why you need this partial class definition, is that by default the &lt;em&gt;Program.cs&lt;/em&gt; file is compiled into a private class &lt;code&gt;Program&lt;/code&gt;, which can not be accessed by other projects. By adding this public partial class, the test project will get access to &lt;code&gt;Program&lt;/code&gt; and lets you write tests against it.&lt;/p&gt;

&lt;p&gt;In the &lt;em&gt;MyMinimalApi.Tests&lt;/em&gt; project, rename the &lt;em&gt;UnitTest1.cs&lt;/em&gt; file to &lt;em&gt;HelloWorldTests.cs&lt;/em&gt; and update the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MyMinimalApi.Tests&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;Microsoft.AspNetCore.Mvc.Testing&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;HelloWorldTests&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;TestRootEndpoint&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 &lt;code&gt;TestRootEndpoint()&lt;/code&gt; test will have to do a couple of things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start the ASP.NET Core Minimal API&lt;/li&gt;
&lt;li&gt;Create an HTTP client for to connect to the application&lt;/li&gt;
&lt;li&gt;Send an HTTP request to the &lt;code&gt;/&lt;/code&gt; endpoint&lt;/li&gt;
&lt;li&gt;Verify the response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Earlier in this post, you have added a reference to the &lt;code&gt;Microsoft.AspNetCore.Mvc.Testing&lt;/code&gt; package. This package contains the &lt;code&gt;WebApplicationFactory&amp;lt;T&amp;gt;&lt;/code&gt;, which is an important building block for testing ASP.NET Core applications.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;WebApplicationFactory&amp;lt;T&amp;gt;&lt;/code&gt; class creates an in-memory application that you can test. It  handles bootstrapping of your application, and provides an &lt;code&gt;HttpClient&lt;/code&gt; that you can use to make requests.&lt;/p&gt;

&lt;p&gt;Update the code in the &lt;code&gt;TestRootEndpoint()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;TestRootEndpoint&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;application&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;WebApplicationFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;&amp;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;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetStringAsync&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;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World!"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code uses &lt;code&gt;WebApplicationFactory&amp;lt;Program&amp;gt;&lt;/code&gt;. Here’s the reason you had to add that public partial class! You can use other public classes from the Minimal API project as well, but I personally prefer &lt;code&gt;Program&lt;/code&gt; as it’s there in every project.&lt;/p&gt;

&lt;p&gt;You can run this test using the .NET CLI, and look at the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dotnet &lt;span class="nb"&gt;test

&lt;/span&gt;Microsoft &lt;span class="o"&gt;(&lt;/span&gt;R&lt;span class="o"&gt;)&lt;/span&gt; Test Execution Command Line Tool Version 17.2.0 &lt;span class="o"&gt;(&lt;/span&gt;x64&lt;span class="o"&gt;)&lt;/span&gt;
Copyright &lt;span class="o"&gt;(&lt;/span&gt;c&lt;span class="o"&gt;)&lt;/span&gt; Microsoft Corporation.  All rights reserved.

Starting &lt;span class="nb"&gt;test &lt;/span&gt;execution, please wait...
A total of 1 &lt;span class="nb"&gt;test &lt;/span&gt;files matched the specified pattern.

Passed!  - Failed:     0, Passed:     1, Skipped:     0, Total:     1, Duration: &amp;lt; 1 ms - MyMinimalApi.Tests.dll &lt;span class="o"&gt;(&lt;/span&gt;net6.0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test you created has just started your Minimal API application using the &lt;code&gt;WebApplicationFactory&amp;lt;Program&amp;gt;&lt;/code&gt;, and uses an &lt;code&gt;HttpClient&lt;/code&gt; that was returned by &lt;code&gt;application.CreateClient()&lt;/code&gt;. Using this client, the test makes an HTTP GET request to the &lt;code&gt;/&lt;/code&gt; endpoint. In this example, you used the &lt;code&gt;GetStringAsync("/")&lt;/code&gt; method to do this. The test then asserts the response matches what is expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Congratulations, you have just created your first test for an ASP.NET Core Minimal API!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Update the Minimal API project
&lt;/h2&gt;

&lt;p&gt;Let’s spice things up a little! In most APIs, endpoints will work with JSON payloads in requests and responses. An API endpoint may return different results depending on the request that is being made. It may return a &lt;code&gt;200 OK&lt;/code&gt; status code on success, and a &lt;code&gt;400 Bad Request&lt;/code&gt; status code with more details in the response body when the request was not valid.&lt;/p&gt;

&lt;p&gt;In this section, you will add such an endpoint to the Minimal API. This endpoint will also perform validation of the request, using the &lt;a href="https://www.nuget.org/packages/MiniValidation/"&gt;MiniValidation package&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Add this package using your favorite IDE, or use the .NET CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add MyMinimalApi package MiniValidation &lt;span class="nt"&gt;--prerelease&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Info:&lt;/strong&gt; &lt;a href="https://github.com/DamianEdwards/MiniValidation"&gt;MiniValidation&lt;/a&gt; is a library intended to bring model validation to ASP.NET Core Minimal APIs. It currently only has pre-release packages available. When a stable version lands you should consider dropping the &lt;code&gt;--prerelease&lt;/code&gt; version.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When that is installed, add a &lt;code&gt;Person&lt;/code&gt; class to your Minimal API. This class will be used as a request payload later on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Required&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;MinLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&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;FirstName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Required&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;MinLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&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;LastName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Required&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;DataType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DataType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the &lt;code&gt;Person&lt;/code&gt; class adds &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations?view=net-6.0"&gt;validation attributes from the &lt;code&gt;System.ComponentModel.DataAnnotations&lt;/code&gt; namespace&lt;/a&gt;. Add &lt;code&gt;using System.ComponentModel.DataAnnotations;&lt;/code&gt; to the top of your &lt;em&gt;Program.cs&lt;/em&gt; file to include the namespace . The &lt;code&gt;MiniValidation&lt;/code&gt; packages you added earlier can process these attributes and validate the request is well-formed.&lt;/p&gt;

&lt;p&gt;The Minimal API will also need to be able to store the &lt;code&gt;Person&lt;/code&gt; in a data store. While modeling this data store is not in the scope of this article, you can define an &lt;code&gt;IPeopleService&lt;/code&gt; interface to interact with the data store, and a &lt;code&gt;PeopleService&lt;/code&gt; class that implements this 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;IPeopleService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="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;PeopleService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPeopleService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;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;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; created."&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Info:&lt;/strong&gt; In real projects, the &lt;code&gt;PeopleService&lt;/code&gt; could use &lt;a href="https://docs.microsoft.com/en-us/ef/core/"&gt;Entity Framework Core&lt;/a&gt; or other storage mechanisms to do something more useful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s now time to register the &lt;code&gt;IPeopleService&lt;/code&gt; with the ASP.NET Core service collection, so your API endpoint can make use of it. Add it as a scoped service to make sure a new instance of &lt;code&gt;PeopleService&lt;/code&gt; is created each time a request comes in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPeopleService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PeopleService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You are doing great! As a final step in this section, you will implement the actual API endpoint in your Minimal API. This endpoint will listen for &lt;code&gt;POST&lt;/code&gt; requests on &lt;code&gt;/people&lt;/code&gt;, and accept a &lt;code&gt;Person&lt;/code&gt; object in the request body. After the endpoint validates the incoming request, the API either uses the &lt;code&gt;IPeopleService&lt;/code&gt; to store the object in the database, or returns a validation result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/people"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IPeopleService&lt;/span&gt; &lt;span class="n"&gt;peopleService&lt;/span&gt;&lt;span class="p"&gt;)&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;MiniValidator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryValidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ValidationProblem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peopleService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;using MiniValidation;&lt;/code&gt; to your using statements at the top of &lt;em&gt;Program.cs&lt;/em&gt; class so you can use the &lt;code&gt;MiniValidator&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;Just to make sure, here’s what your &lt;em&gt;Program.cs&lt;/em&gt; should now 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;using&lt;/span&gt; &lt;span class="nn"&gt;System.ComponentModel.DataAnnotations&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;MiniValidation&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;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPeopleService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PeopleService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&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;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Hello World!"&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;MapPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/people"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IPeopleService&lt;/span&gt; &lt;span class="n"&gt;peopleService&lt;/span&gt;&lt;span class="p"&gt;)&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;MiniValidator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryValidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ValidationProblem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peopleService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

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

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&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;interface&lt;/span&gt; &lt;span class="nc"&gt;IPeopleService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="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;PeopleService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPeopleService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;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;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; created."&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;Person&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Required&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;MinLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&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;FirstName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Required&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;MinLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&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;LastName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Required&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;DataType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DataType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to, you can run the Minimal API and test the &lt;code&gt;/people&lt;/code&gt; endpoint from your terminal`.&lt;/p&gt;

&lt;p&gt;First, start your Minimal API using &lt;code&gt;dotnet run --project MyMinimalApi&lt;/code&gt; and look for the localhost URL in the output.&lt;/p&gt;

&lt;p&gt;If you have the &lt;code&gt;curl&lt;/code&gt; command available in your terminal, run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
curl -X POST --location "https://localhost:7230/people" \&lt;br&gt;
    -H "Content-Type: application/json" \&lt;br&gt;
    -d "{ \"FirstName\": \"Maarten\" }"&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Or if you're using PowerShell, run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;powershell&lt;br&gt;
Invoke-WebRequest&lt;/code&gt;&lt;br&gt;
    -Uri &lt;a href="https://localhost:7230/people"&gt;https://localhost:7230/people&lt;/a&gt; &lt;code&gt;&lt;br&gt;
    -Method Post&lt;/code&gt;&lt;br&gt;
    -ContentType "application/json" &lt;code&gt;&lt;br&gt;
    -Body '{"FirstName": "Maarten"}'&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Replace the &lt;code&gt;https://localhost:7230&lt;/code&gt; with the localhost URL that the &lt;code&gt;dotnet run&lt;/code&gt; command printed to the console.&lt;/p&gt;

&lt;p&gt;The response should be a &lt;code&gt;400 Bad request&lt;/code&gt;, since the &lt;code&gt;LastName&lt;/code&gt; and &lt;code&gt;Email&lt;/code&gt; properties are required:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;br&gt;
HTTP/1.1 400 Bad Request&lt;br&gt;
Content-Type: application/problem+json&lt;br&gt;
Date: Fri, 03 Jun 2022 09:04:56 GMT&lt;br&gt;
Server: Kestrel&lt;br&gt;
Transfer-Encoding: chunked&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "type": "&lt;a href="https://tools.ietf.org/html/rfc7231#section-6.5.1"&gt;https://tools.ietf.org/html/rfc7231#section-6.5.1&lt;/a&gt;",&lt;br&gt;
  "title": "One or more validation errors occurred.",&lt;br&gt;
  "status": 400,&lt;br&gt;
  "errors": {&lt;br&gt;
    "LastName": [&lt;br&gt;
      "The LastName field is required."&lt;br&gt;
    ],&lt;br&gt;
    "Email": [&lt;br&gt;
      "The Email field is required."&lt;br&gt;
    ]&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After you confirm the endpoint works, you will convert this request into a test!&lt;/p&gt;

&lt;h2&gt;
  
  
  Test different payloads and HTTP methods
&lt;/h2&gt;

&lt;p&gt;Your Minimal API now has a &lt;code&gt;/people&lt;/code&gt; endpoint. It has two possible response types: a &lt;code&gt;200 OK&lt;/code&gt; that returns a string value, and a &lt;code&gt;400 Bad Request&lt;/code&gt; that returns problem details as a JSON payload.&lt;/p&gt;

&lt;p&gt;In the &lt;em&gt;MyMinimalApi.Tests&lt;/em&gt; project, add a &lt;em&gt;PeopleTests.cs&lt;/em&gt; file that contains the following code:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`csharp&lt;br&gt;
using System.Net;&lt;br&gt;
using System.Net.Http.Json;&lt;br&gt;
using Microsoft.AspNetCore.Http;&lt;br&gt;
using Microsoft.AspNetCore.Mvc.Testing;&lt;/p&gt;

&lt;p&gt;namespace MyMinimalApi.Tests;&lt;/p&gt;

&lt;p&gt;public class PeopleTests&lt;br&gt;
{&lt;br&gt;
    [Fact]&lt;br&gt;
    public async Task CreatePerson()&lt;br&gt;
    {&lt;br&gt;
    }&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Fact]
public async Task CreatePersonValidatesObject()
{
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;PeopleTests&lt;/code&gt; class now contains 2 test methods that you will need to implement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CreatePerson()&lt;/code&gt; to test the &lt;code&gt;200 OK&lt;/code&gt; scenario&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CreatePersonValidatesObject()&lt;/code&gt; to test the &lt;code&gt;400 Bad Request&lt;/code&gt; scenario&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You will start with the &lt;code&gt;CreatePerson()&lt;/code&gt; test method. The test will again make use of the &lt;code&gt;WebApplicationFactory&amp;lt;Program&amp;gt;&lt;/code&gt; to create an in-memory HTTP client that you can use to validate the API.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`csharp&lt;br&gt;
[Fact]&lt;br&gt;
public async Task CreatePerson()&lt;br&gt;
{&lt;br&gt;
    await using var application = new WebApplicationFactory();&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var client = application.CreateClient();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next, you will use the &lt;code&gt;client&lt;/code&gt; to send a JSON payload to the &lt;code&gt;/people&lt;/code&gt; endpoint. You can use the &lt;code&gt;PostAsJsonAsync()&lt;/code&gt; method to send a JSON payload to the Minimal API under test. Finally, you can use the xUnit &lt;code&gt;Assert&lt;/code&gt; class to validate the response status code and the response content.&lt;/p&gt;

&lt;p&gt;Update the &lt;code&gt;CreatePerson()&lt;/code&gt; test like below:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`csharp&lt;br&gt;
[Fact]&lt;br&gt;
public async Task CreatePerson()&lt;br&gt;
{&lt;br&gt;
    await using var application = new WebApplicationFactory();&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var client = application.CreateClient();

var result = await client.PostAsJsonAsync("/people", new Person
{
    FirstName = "Maarten",
    LastName = "Balliauw",
    Email = "maarten@jetbrains.com"
});

Assert.Equal(HttpStatusCode.OK, result.StatusCode);
Assert.Equal("\"Maarten Balliauw created.\"", await result.Content.ReadAsStringAsync());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can run this test using the .NET CLI, and confirm your Minimal API works as expected.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
dotnet test&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;CreatePersonValidatesObject()&lt;/code&gt; test is next. Like in the &lt;code&gt;CreatePerson()&lt;/code&gt; test method, you will begin with creating a request to the in-memory Minimal API. Only this time, you will send an empty &lt;code&gt;Person&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;Since all of its properties will be &lt;code&gt;null&lt;/code&gt; or empty, the test should get back a &lt;code&gt;400 Bad Request&lt;/code&gt;. You can assert this is indeed the case. What’s more, you can also use the &lt;code&gt;result.Content.ReadFromJsonAsync&amp;lt;&amp;gt;()&lt;/code&gt; method to deserialize the validation problems, and verify they are as expected.&lt;/p&gt;

&lt;p&gt;Update the &lt;code&gt;CreatePersonValidatesObject()&lt;/code&gt; test like below:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`csharp&lt;br&gt;
[Fact]&lt;br&gt;
public async Task CreatePersonValidatesObject()&lt;br&gt;
{&lt;br&gt;
    await using var application = new WebApplicationFactory();&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var client = application.CreateClient();

var result = await client.PostAsJsonAsync("/people", new Person());

Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode);

var validationResult = await result.Content.ReadFromJsonAsync&amp;lt;HttpValidationProblemDetails&amp;gt;();
Assert.NotNull(validationResult);
Assert.Equal("The FirstName field is required.", validationResult!.Errors["FirstName"][0]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I will leave the validation of the other properties as an exercise for you.&lt;/p&gt;

&lt;p&gt;Again, try running this test using the .NET CLI, and confirm your Minimal API works as expected.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
dotnet test&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Well done! You have now written tests that validate JSON payloads accepted and returned by your Minimal API!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Customizing the service collection
&lt;/h2&gt;

&lt;p&gt;There’s one more thing… The Minimal API you created contains a &lt;code&gt;PeopleService&lt;/code&gt; that, in a more real-life project, could need a database connection. This could be okay for some tests, and unnecessary for others.&lt;/p&gt;

&lt;p&gt;The tests that you have written so far all have been validating the responses of the Minimal API. There’s no real need for the “real” implementation of &lt;code&gt;IPeopleService&lt;/code&gt;, so let’s see how you can swap it out with a test implementation!&lt;/p&gt;

&lt;p&gt;In the &lt;em&gt;MyMinimalApi.Tests&lt;/em&gt; project, create a new file &lt;em&gt;TestPeopleService.cs&lt;/em&gt; with the following code:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;csharp&lt;br&gt;
public class TestPeopleService : IPeopleService&lt;br&gt;
{&lt;br&gt;
   public string Create(Person person) =&amp;gt; "It works!";&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;TestPeopleService&lt;/code&gt; class implements &lt;code&gt;IPeopleService&lt;/code&gt; just like the real implementation does, but the &lt;code&gt;Create&lt;/code&gt; method returns a simple &lt;code&gt;string&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;Next, you will update the test methods to configure the &lt;code&gt;WebApplicationFactory&amp;lt;Program&amp;gt;&lt;/code&gt; with a service override for &lt;code&gt;IPeopleService&lt;/code&gt;, wiring it to &lt;code&gt;TestPeopleService&lt;/code&gt; instead. You can do this in a number of ways: using the &lt;code&gt;WithWebHostBuilder()&lt;/code&gt; and &lt;code&gt;ConfigureServices()&lt;/code&gt; methods, or by implementing a custom &lt;code&gt;WebApplicationFactory&amp;lt;T&amp;gt;&lt;/code&gt;. In this tutorial, you will use the first approach to change the &lt;code&gt;IPeopleService&lt;/code&gt; to be a &lt;code&gt;TestPeopleService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Update the &lt;code&gt;CreatePerson&lt;/code&gt; test with the following code:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`csharp&lt;br&gt;
[Fact]&lt;br&gt;
public async Task CreatePerson()&lt;br&gt;
{&lt;br&gt;
   await using var application = new WebApplicationFactory()&lt;br&gt;
       .WithWebHostBuilder(builder =&amp;gt; builder&lt;br&gt;
           .ConfigureServices(services =&amp;gt;&lt;br&gt;
           {&lt;br&gt;
               services.AddScoped();&lt;br&gt;
           }));&lt;/p&gt;

&lt;p&gt;var client = application.CreateClient();&lt;/p&gt;

&lt;p&gt;var result = await client.PostAsJsonAsync("/people", new Person&lt;br&gt;
   {&lt;br&gt;
       FirstName = "Maarten",&lt;br&gt;
       LastName = "Balliauw",&lt;br&gt;
       Email = "&lt;a href="mailto:maarten@jetbrains.com"&gt;maarten@jetbrains.com&lt;/a&gt;"&lt;br&gt;
   });&lt;/p&gt;

&lt;p&gt;Assert.Equal(HttpStatusCode.OK, result.StatusCode);&lt;br&gt;
   Assert.Equal("\"It works!\"", await result.Content.ReadAsStringAsync());&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To use &lt;code&gt;services.AddScoped&lt;/code&gt;, add &lt;code&gt;using Microsoft.Extensions.DependencyInjection;&lt;/code&gt; to your using statements at the top of the file.&lt;/p&gt;

&lt;p&gt;Note that in the code sample, the final &lt;code&gt;Assert.Equal&lt;/code&gt; is now testing for the &lt;code&gt;string&lt;/code&gt; that is returned by &lt;code&gt;TestPeopleService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Depending on how many customizations you want to make to your Minimal API under test, you can move the &lt;code&gt;WithWebHostBuilder()&lt;/code&gt; and &lt;code&gt;ConfigureServices()&lt;/code&gt; methods out, and override the &lt;code&gt;WebApplicationFactory&amp;lt;T&amp;gt;&lt;/code&gt; class. This has the advantage of having one place where you customize the service collection.&lt;/p&gt;

&lt;p&gt;For example, you can create a &lt;code&gt;TestingApplication&lt;/code&gt; class and override the &lt;code&gt;CreateHost&lt;/code&gt; method to customize the service collection:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`csharp&lt;br&gt;
class TestingApplication : WebApplicationFactory&lt;br&gt;
{&lt;br&gt;
   protected override IHost CreateHost(IHostBuilder builder)&lt;br&gt;
   {&lt;br&gt;
       builder.ConfigureServices(services =&amp;gt;&lt;br&gt;
       {&lt;br&gt;
           services.AddScoped();&lt;br&gt;
       });&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   return base.CreateHost(builder);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can use it in tests by replacing &lt;code&gt;new WebApplicationFactory&amp;lt;Program&amp;gt;&lt;/code&gt; with &lt;code&gt;new TestingApplication()&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`csharp&lt;br&gt;
[Fact]&lt;br&gt;
public async Task CreatePerson()&lt;br&gt;
{&lt;br&gt;
   await using var application = new TestingApplication();&lt;/p&gt;

&lt;p&gt;var client = application.CreateClient();&lt;/p&gt;

&lt;p&gt;var result = await client.PostAsJsonAsync("/people", new Person&lt;br&gt;
   {&lt;br&gt;
       FirstName = "Maarten",&lt;br&gt;
       LastName = "Balliauw",&lt;br&gt;
       Email = "&lt;a href="mailto:maarten@jetbrains.com"&gt;maarten@jetbrains.com&lt;/a&gt;"&lt;br&gt;
   });&lt;/p&gt;

&lt;p&gt;Assert.Equal(HttpStatusCode.OK, result.StatusCode);&lt;br&gt;
   Assert.Equal("\"It works!\"", await result.Content.ReadAsStringAsync());&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you want to start customizing the Minimal API during tests, make sure to explore the various methods of &lt;code&gt;WebApplicationFactory&amp;lt;T&amp;gt;&lt;/code&gt; that you can override to configure your application for the tests you are writing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That’s it! You just built several tests for an ASP.NET Core Minimal API, and validated it behaves as expected. You started out with testing a basic endpoint that returned a string, and then saw how to work with different HTTP methods and payloads on the request and response. You even customized the ASP.NET Core service collection with custom services for your tests.&lt;/p&gt;

&lt;p&gt;Whether you are writing unit tests, integration tests or both, you should now have a good understanding of how to go about using the test server and customizing the service collection for many scenarios.&lt;/p&gt;

&lt;p&gt;If you’re hungry for more, check out the &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests"&gt;Microsoft docs on integration testing&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>aspnetcore</category>
      <category>testing</category>
    </item>
    <item>
      <title>Techniques and tools to update your C# project - Migrating to nullable reference types - Part 4</title>
      <dc:creator>Maarten Balliauw</dc:creator>
      <pubDate>Tue, 03 May 2022 02:44:05 +0000</pubDate>
      <link>https://dev.to/maartenba/techniques-and-tools-to-update-your-c-project-migrating-to-nullable-reference-types-part-4-2ha1</link>
      <guid>https://dev.to/maartenba/techniques-and-tools-to-update-your-c-project-migrating-to-nullable-reference-types-part-4-2ha1</guid>
      <description>&lt;p&gt;Previously, &lt;a href="https://dev.to/maartenba/annotating-your-c-code-migrating-to-nullable-reference-types-part-3-40k6"&gt;we saw how you can help the compiler’s flow analysis understand your code&lt;/a&gt;, by annotating your code for nullability.&lt;/p&gt;

&lt;p&gt;In this final post of our series, we’ll have a look at the techniques and tools that are available to migrate to using nullable reference types in an existing code base.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pick your approach: there is no silver bullet
&lt;/h2&gt;

&lt;p&gt;As we have seen in &lt;a href="https://blog.maartenballiauw.be/post/2022/04/19/internals-of-csharp-nullable-reference-types-migrating-to-nullable-reference-types-part-2.html#which-nullable-annotation-context-should-you-use"&gt;a previous post&lt;/a&gt;, it can be an overwhelming experience to go all-in and enable the nullable annotation context for all projects in your solution.&lt;/p&gt;

&lt;p&gt;Generally speaking, it’s a good idea to fully enable the nullable annotation context for new projects. This gets you the benefits of better static flow analysis from the start.&lt;/p&gt;

&lt;p&gt;For existing projects, the choice is yours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For smaller projects, you can set &lt;code&gt;&amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;&lt;/code&gt; at the project level and plow through.&lt;/li&gt;
&lt;li&gt;For larger projects, you may want to leave the project level set to &lt;code&gt;disable&lt;/code&gt;, and add &lt;code&gt;#nullable enable&lt;/code&gt; file by file.&lt;/li&gt;
&lt;li&gt;Alternatively, you can set &lt;code&gt;warnings&lt;/code&gt; as the project level default, so you’ll see warnings where the compiler’s flow analysis infers potential &lt;code&gt;null&lt;/code&gt; references. You can then add &lt;code&gt;#nullable enable&lt;/code&gt; file by file, and gradually add the right annotations and nullable attributes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regardless of the setting you choose at the project level, you’ll be in the mode of working through all warnings incrementally.&lt;/p&gt;

&lt;p&gt;There is no silver bullet. There are, however, some techniques and tools that will help you reach the end goal of having a fully annotated codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start at the edges and work inwards
&lt;/h2&gt;

&lt;p&gt;Where to begin? What worked well for me on various code bases, was to start at the edges.&lt;/p&gt;

&lt;p&gt;Try and find the classes in your project that have zero dependencies on other reference types, apart from some strings. Data Transfer Objects (DTOs) / Plain-Old CLR Objects (POCOs) almost always fall under this category.&lt;/p&gt;

&lt;p&gt;DTOs/POCOs are often used in many places throughout your project. Updating nullability for these classes means that nullability flows through the rest of your projects, and makes usages more reliable project-wide. So even if annotating one property at the edge seems like a small thing to do, it will flow through and be meaningful in the bigger picture.&lt;/p&gt;

&lt;p&gt;Here’s an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LocationInfo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Country&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Location&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;LocationInfo&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;country&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;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Country&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;Location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;location&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 &lt;code&gt;LocationInfo&lt;/code&gt; class only has two properties. Converting this class to using C# nullable reference types may be easy!&lt;/p&gt;

&lt;h3&gt;
  
  
  Add annotations or redesign your code
&lt;/h3&gt;

&lt;p&gt;Let’s enable nullable reference types for this &lt;code&gt;LocationInfo&lt;/code&gt; class!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;#nullable enable&lt;/code&gt; to the class file&lt;/li&gt;
&lt;li&gt;In the IDE, use &lt;strong&gt;Find Usages&lt;/strong&gt; on every property, and determine if the properties are potentially set to &lt;code&gt;null&lt;/code&gt; anywhere. 

&lt;ul&gt;
&lt;li&gt;If there’s a value specified at every usage, keep the class as-is.&lt;/li&gt;
&lt;li&gt;If a &lt;code&gt;null&lt;/code&gt; reference is passed in, you may need to annotate the property with &lt;code&gt;?&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;In the IDE, use &lt;strong&gt;Find Usages&lt;/strong&gt; on the constructor, and determine if the constructor parameters are potentially set to &lt;code&gt;null&lt;/code&gt; anywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Going through usages, I found this particular case in my code base:&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_databaseReader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryCity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&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="p"&gt;&amp;amp;&amp;amp;&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;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="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LocationInfo&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;Country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;City&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I really want to recommend using &lt;a href="https://www.jetbrains.com/resharper/"&gt;ReSharper (R#)&lt;/a&gt; or &lt;a href="https://www.jetbrains.com/rider"&gt;JetBrains Rider&lt;/a&gt; once more. As mentioned before, both tools ship years of experience with nullable flow analysis, and it shows.&lt;/p&gt;

&lt;p&gt;Both R# and Rider caught that &lt;code&gt;result.Country.Name&lt;/code&gt; and &lt;code&gt;result.City.Name&lt;/code&gt; may be &lt;code&gt;null&lt;/code&gt;, even with the nullable warning context set to &lt;code&gt;disabled&lt;/code&gt; at the project level:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_IhYGebC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/rider-ide-non-nullable-entity.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_IhYGebC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/rider-ide-non-nullable-entity.png" alt="Possible 'null' assignment to non-nullable entity" width="880" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is one of those cases where you’ll have to decide on the approach to take…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should you annotate the &lt;code&gt;LocationInfo&lt;/code&gt; constructor parameters?&lt;/li&gt;
&lt;li&gt;Should you keep the &lt;code&gt;LocationInfo&lt;/code&gt; constructor parameters as non-nullable and update the call site?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this case there is only one call site that potentially passes a &lt;code&gt;null&lt;/code&gt; reference, so let’s keep the &lt;code&gt;LocationInfo&lt;/code&gt; constructor parameters non-nullable and update the call site instead:&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_databaseReader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryCity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&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="p"&gt;&amp;amp;&amp;amp;&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;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&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;Country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&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;City&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="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="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LocationInfo&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;Country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;City&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;LocationInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The call site is updated with more thorough &lt;code&gt;null&lt;/code&gt; checks, and a redesigned API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When no &lt;code&gt;null&lt;/code&gt; values are present, we still return &lt;code&gt;LocationInfo&lt;/code&gt; with non-nullable properties.&lt;/li&gt;
&lt;li&gt;When any values are &lt;code&gt;null&lt;/code&gt;, we return &lt;code&gt;LocationInfo.Unknown&lt;/code&gt; - a static property with both properties set to &lt;code&gt;"Unknown"&lt;/code&gt;. No need for &lt;code&gt;null&lt;/code&gt; checks anywhere &lt;code&gt;LocationInfo&lt;/code&gt; is used, there’s always going to be a value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Much like with &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;, nullable annotations will flow through your entire project. If we annotated the properties of &lt;code&gt;LocationInfo&lt;/code&gt; as being nullable, we’d have to do &lt;code&gt;null&lt;/code&gt; checks in our entire project. Instead, we chose to redesign our code and set a boundary of how far potential &lt;code&gt;null&lt;/code&gt; references can flow. In this case, not far at all.&lt;/p&gt;

&lt;p&gt;Once again, keep in mind there’s no silver bullet. In some cases, adding a nullable annotation will be the way to go, in other cases a small (or big) redesign may be better.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don’t be afraid of &lt;code&gt;null&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Before we continue, there’s something important to keep in mind. The goal of migrating to C# nullable reference types, is to gain more confidence in the flow analysis provided by the compiler and the IDE. We’re not here to completely get rid of all &lt;code&gt;null&lt;/code&gt; usages in our code!&lt;/p&gt;

&lt;p&gt;As part of migrating to nullable reference types, you will be annotating some reference types with &lt;code&gt;?&lt;/code&gt;, sometimes you’ll be suppressing warnings with &lt;code&gt;!&lt;/code&gt;, and sometimes, you’ll end up redesigning bits of your code.&lt;/p&gt;

&lt;p&gt;Returning or passing around &lt;code&gt;null&lt;/code&gt; is totally fine. Using C# nullable reference types and annotations makes doing so more reliable, with fewer chances of &lt;code&gt;NullReferenceException&lt;/code&gt; being thrown unexpectedly. We’re building a safety net.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nullable warning suppressions should be temporary
&lt;/h2&gt;

&lt;p&gt;As part of migration, you may sprinkle some null-forgiving operators through your project’s code. When you’re not sure a reference type should be nullable or not, you can suffix the usage with the dammit-operator, &lt;code&gt;!&lt;/code&gt;, and suppress any nullability warnings for that code path.&lt;/p&gt;

&lt;p&gt;What’s nice about nullable warning suppressions, is that they disables flow analysis for a certain code path. You can use it to see the effect of what would happen to a code path if a reference that currently can be &lt;code&gt;null&lt;/code&gt; would be redefined as non-nullable.&lt;/p&gt;

&lt;p&gt;In some cases, you will indeed need to suppress &lt;code&gt;null&lt;/code&gt;, and its usage is valid. In most cases, however, consider nullable warning suppressions a code smell. Using &lt;code&gt;!&lt;/code&gt; should be a temporary thing, use it with care. Since it disables flow analysis, it could hide nullability issues in your project - the exact issue you set out to improve upon!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tool tip:&lt;/strong&gt; At any time during a migration, you can use ReSharper or JetBrains Rider to find all nullable warning suppressions. Use &lt;strong&gt;Alt+Enter&lt;/strong&gt; on any suppression, and search for other suppressions in the current file, project, or solution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cqopGqwe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/rider-find-nullable-warning-suppressions.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cqopGqwe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/rider-find-nullable-warning-suppressions.png" alt="Rider - Find nullable warning suppressions in project or solution" width="880" height="685"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where is this value coming from? Where is it being used?
&lt;/h2&gt;

&lt;p&gt;In many cases, &lt;strong&gt;Find Usages&lt;/strong&gt; will be sufficient to get an idea of the direct usages of a class, constructor, method, or property. In other cases, you may need more information.&lt;/p&gt;

&lt;p&gt;ReSharper (R#) and JetBrains Rider come with &lt;a href="https://www.jetbrains.com/help/resharper/Code_Analysis__Value_Tracking.html"&gt;value tracking&lt;/a&gt; and &lt;a href="https://www.jetbrains.com/help/resharper/Code_Analysis__Call_Tracking.html"&gt;call tracking&lt;/a&gt; to help you out here.&lt;a href="https://www.visualstudio.com/"&gt;Visual Studio 2022&lt;/a&gt; also has a &lt;em&gt;Track Value Source&lt;/em&gt; command, but your mileage with it will vary.&lt;/p&gt;

&lt;p&gt;With value tracking, you can follow the entire flow of a specific value and determine where it is originating from and where it is being used.&lt;/p&gt;

&lt;p&gt;Not sure if this &lt;code&gt;TrackingAccount&lt;/code&gt; property should be annotated? The &lt;strong&gt;Inspect | Value Origin&lt;/strong&gt; action will track all places where a value for this property can be assigned, and provides a tool window to jump to every location.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wtscEepn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/resharper-value-origin-tracking.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wtscEepn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/resharper-value-origin-tracking.png" alt="ReSharper Value Origin Tracking" width="880" height="699"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that it’s also possible to do the inverse, and analyze where the value from this &lt;code&gt;TrackingAccount&lt;/code&gt; property is used.&lt;/p&gt;

&lt;h2&gt;
  
  
  JetBrains Annotations to C# nullable annotations
&lt;/h2&gt;

&lt;p&gt;In a &lt;a href="https://blog.maartenballiauw.be/post/2022/04/25/annotating-your-csharp-code-migrating-to-nullable-reference-types-part-3.html#jetbrains-annotations"&gt;previous post&lt;/a&gt;, we discussed JetBrains Annotations already. If you’re working on a project where these annotations were already in use before C# introduced nullable reference types, you are in luck when migrating to C#’s version!&lt;/p&gt;

&lt;p&gt;When you enable the nullable context, ReSharper and JetBrains Rider will help you with the migration. You’ll get hints on whether certain annotations are still needed.&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="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;NotNull&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;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ReadColumnFromCsv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;CsvReader&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CanBeNull&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;columnName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;NotNull&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;defaultValue&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="p"&gt;!&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;defaultValue&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 &lt;code&gt;ReadColumnFromCsv&lt;/code&gt; returns a non-nullable &lt;code&gt;string&lt;/code&gt;, which means the &lt;code&gt;[NotNull]&lt;/code&gt; annotation can be safely removed. The &lt;strong&gt;Remove redundant attribute&lt;/strong&gt; quick fix is one Alt+Enter away!&lt;/p&gt;

&lt;p&gt;Similarly, the &lt;code&gt;[CanBeNull] string columnName&lt;/code&gt; parameter declaration can be updated. The original &lt;code&gt;[CanBeNull]&lt;/code&gt; annotation can be removed, and converted to &lt;code&gt;string? columnName&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MDR5HAMc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/use-type-annotation-syntax.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MDR5HAMc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/use-type-annotation-syntax.png" alt="Use type annotation syntax" width="880" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re using the 2022.1 version of ReSharper or JetBrains Rider, there is a new &lt;strong&gt;Migrate to #nullable enable&lt;/strong&gt; quick fix that does a few things at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It inserts all &lt;code&gt;[NotNull]&lt;/code&gt; and &lt;code&gt;[CanBeNull]&lt;/code&gt; attributes inherited from base members such as implemented interfaces. JetBrains annotations can be inherited (unlike C#’s annotations), so they are pulled in.&lt;/li&gt;
&lt;li&gt;It infers annotations, by looking at your code’s branches. Are you returning &lt;code&gt;null&lt;/code&gt;? A nullable return type will be inferred.&lt;/li&gt;
&lt;li&gt;It converts all JetBrains Annotations in th current file to C# annotations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oGD7RtV4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/migrate-to-nullable-enable.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oGD7RtV4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/migrate-to-nullable-enable.png" alt="Migrate to #nullable enable" width="880" height="251"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; You can run most of these quick fixes on your entire file, project or solution in one go.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Determine nullability based on &lt;code&gt;null&lt;/code&gt; checks
&lt;/h2&gt;

&lt;p&gt;When you’re annotating your code, there are often clear hints in your code about what its nullability should be.&lt;/p&gt;

&lt;p&gt;Here’s a quiz: in the following &lt;code&gt;ReadColumnFromExcel&lt;/code&gt; method, what should the nullability of the &lt;code&gt;columnName&lt;/code&gt; parameter be?&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="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ReadColumnFromExcel&lt;/span&gt;&lt;span class="p"&gt;(&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;int&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;&amp;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;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;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mappings&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;columnName&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;defaultValue&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;columnName&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mappings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnName&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;columnIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnIndex&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;columnData&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;columnData&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;defaultValue&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;return&lt;/span&gt; &lt;span class="n"&gt;defaultValue&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;If you answered &lt;code&gt;string? columnName&lt;/code&gt;, you are right!&lt;/p&gt;

&lt;p&gt;The first line of code in this method is checking if &lt;code&gt;columnName != null&lt;/code&gt;, which means it should be annotated as nullable. There will be lots of these cases in the project you are migrating, and they usually provide a great hint in terms of annotating a parameter or property.&lt;/p&gt;

&lt;p&gt;ReSharper and JetBrains Rider will detect these cases for you, and offer to fix the annotation(s) for you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H9dEPpQ3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/change-type-of-parameter.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H9dEPpQ3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/change-type-of-parameter.png" alt="Change type of parameter to nullable" width="880" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What about third-party libraries and external code?
&lt;/h2&gt;

&lt;p&gt;When you are consuming third-party libraries, you’re in for a treat! Looking at the top downloaded packages on &lt;a href="https://www.nuget.org/"&gt;NuGet.org&lt;/a&gt;, not all of them are annotated. I’m sure if you look at the long tail of packages, there will be many more libraries that are not annotated at all!&lt;/p&gt;

&lt;h3&gt;
  
  
  Libraries with C# annotations
&lt;/h3&gt;

&lt;p&gt;If you’re lucky, the library you are consuming has been fully annotated. There’s not much to say in this case: the C# compiler and all IDEs will pick up these annotations, and give you design- and compile-time hints. Great!&lt;/p&gt;

&lt;h3&gt;
  
  
  Libraries with JetBrains Annotations
&lt;/h3&gt;

&lt;p&gt;If you’re consuming a library that ships its JetBrains Annotations, and you are using ReSharper or JetBrains Rider, you’re in luck as well.&lt;/p&gt;

&lt;p&gt;ReSharper and JetBrains Rider will automatically recognize annotations found in the &lt;code&gt;JetBrains.Annotations&lt;/code&gt; namespace. Sometimes, libraries ship a custom namespace. The IDE will recognize these attributes, but you’ll still need to enable them in the settings.&lt;/p&gt;

&lt;p&gt;Here’s an example with &lt;a href="https://www.hangfire.io/"&gt;Hangfire&lt;/a&gt;. This project ships their annotations in the &lt;code&gt;Hangfire.Annotations&lt;/code&gt; namespace:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wwu4szKd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/enable-jetbrains-annotations.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wwu4szKd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/enable-jetbrains-annotations.png" alt="Enable Hangfire annotations" width="880" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After enabling it, the IDE considers the annotations in flow analysis:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e-0W_YMk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/jetbrains-annotations-analysis.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e-0W_YMk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/jetbrains-annotations-analysis.png" alt="JetBrains Annotations used in flow analysis" width="880" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While many libraries use JetBrains Annotations, not all of them ship them in their NuGet package.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If you have a library that is annotated with JetBrains annotations, &lt;a href="https://blog.jetbrains.com/dotnet/2015/08/12/how-to-use-jetbrains-annotations-to-improve-resharper-inspections/"&gt;make sure to ship them along with your code&lt;/a&gt; and make the life of many developers more enjoyable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Libraries without annotations
&lt;/h3&gt;

&lt;p&gt;If you are consuming libraries that are not annotated with either C#’s or JetBrains’ nullable annotations, you’ll have to do lots of &lt;code&gt;null&lt;/code&gt; checks. Unless you dive into their source code, there is no way the compiler’s flow analysis can give you reliable hints.&lt;/p&gt;

&lt;p&gt;If you’re using ReSharper or JetBrains Rider, you can enable pessimistic analysis to help uncover the places where you’ll need extra &lt;code&gt;null&lt;/code&gt; checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pessimistic analysis
&lt;/h2&gt;

&lt;p&gt;By default, ReSharper and JetBrains Rider analyze your code in &lt;em&gt;optimistic&lt;/em&gt; mode. In this mode, you will only see warnings about potentially dereferencing &lt;code&gt;null&lt;/code&gt; if you explicitly checked it for &lt;code&gt;null&lt;/code&gt; in the code path, or if it’s annotated as nullable.&lt;/p&gt;

&lt;p&gt;The opposite mode is &lt;em&gt;pessimistic&lt;/em&gt;. Unless a value is annotated as non-nullable, the IDE will expect you do a &lt;code&gt;null&lt;/code&gt; check. The web help has &lt;a href="https://www.jetbrains.com/help/resharper/Code_Analysis__Value_Analysis.html#modes_"&gt;more info about both modes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s an example. This &lt;code&gt;ReadColumnFromCsv&lt;/code&gt; returns a non-null &lt;code&gt;string&lt;/code&gt;. In pessimistic mode, you’ll see a warning when returning &lt;code&gt;csv[columnName]&lt;/code&gt;. The &lt;code&gt;CsvReader&lt;/code&gt;’s indexer has no nullable annotations, and therefore pessimistic analysis treats it as a potential &lt;code&gt;null&lt;/code&gt; reference.&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="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ReadColumnFromCsv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;CsvReader&lt;/span&gt; &lt;span class="n"&gt;csv&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;columnName&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;defaultValue&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="p"&gt;!&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// Considered nullable in pessimistic mode&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;defaultValue&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;To get rid of this warning, you’ll have to return the &lt;code&gt;defaultValue&lt;/code&gt; when &lt;code&gt;csv[columnName]&lt;/code&gt; is null:&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;return&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnName&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;csv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;columnName&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;csv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;defaultValue&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pessimistic analysis is not a mode I would recommend as the default in your projects. It’s more pessimistic than the compiler is!&lt;/p&gt;

&lt;p&gt;It may be of help when migrating to C# nullable reference types, as it will definitely uncover some cases where you need additional &lt;code&gt;null&lt;/code&gt; checks. Especially when working with third-party libraries that may not (yet) be annotated!&lt;/p&gt;

&lt;h2&gt;
  
  
  Deserializing JSON
&lt;/h2&gt;

&lt;p&gt;If you have played with nullable reference types already, you may have found that there is no good way to get rid of all nullability warnings.&lt;/p&gt;

&lt;p&gt;Typically, you will have several classes that will be used when deserializing JSON data, something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With C# nullable reference types enabled, a warning will be shown for the &lt;code&gt;Name&lt;/code&gt; property: &lt;em&gt;Non-nullable property is uninitialized. Consider declaring it as nullable.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s look at how we can fix these warnings…&lt;/p&gt;

&lt;h3&gt;
  
  
  Make the property nullable - Bad!
&lt;/h3&gt;

&lt;p&gt;Following the compiler’s advice, you can update the property and make it nullable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done! No more warnings! However, you now have to check for &lt;code&gt;Name != null&lt;/code&gt; anywhere you consume this property…&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a default value and suppress the warning - Bad!
&lt;/h3&gt;

&lt;p&gt;Another option would be to suppress the warning, and change the &lt;code&gt;User&lt;/code&gt; class to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&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;Done! No more warnings! However, you are now lying to the compiler. When consuming the &lt;code&gt;Name&lt;/code&gt; property, you may get a &lt;code&gt;null&lt;/code&gt; reference after all.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a primary constructor (Newtonsoft.Json) - Good!
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;Newtonsoft.Json&lt;/code&gt;, you can add a primary constructor that covers all properties of your class. The JSON deserializer will pick this up, and calls the constructor instead of setting the properties directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;User&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;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"Unknown"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// or throw ArgumentNullException&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;init&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;With this approach, you’ll get rid of nullability warnings without shooting yourself in the foot. If you don’t expect a &lt;code&gt;null&lt;/code&gt; value from the JSON, stay close to that expectation and declare the property as non-nullable. In the constructor, you can set a default value, or throw an &lt;code&gt;ArgumentNullException&lt;/code&gt;. Don’t blindly accept and propagate &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Annotations and default values - Good!
&lt;/h3&gt;

&lt;p&gt;Another approach to our problem would be setting a proper default value. In case no value is deserialized, and assuming the JSON deserializer doesn’t explicitly pass in &lt;code&gt;null&lt;/code&gt; in such case, the property will be non-nullable and contain an expected default value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;init&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="s"&gt;"Unknown"&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 can also be accomplished using &lt;code&gt;record&lt;/code&gt; classes, which is quite elegant for objects that are solely used for JSON deserialization:&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="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&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;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Unknown"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An alternative would be to use a backing field, and make use of the &lt;code&gt;[AllowNull]&lt;/code&gt; attribute that lets callers set &lt;code&gt;null&lt;/code&gt;, while being certain that they will never get &lt;code&gt;null&lt;/code&gt; when reading from this property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AllowNull&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"Unknown"&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;Personally, I would not recommend this specific approach. It gets rid of all warnings, but it’s cumbersome to maintain with that backing field. And more importantly, since the &lt;code&gt;[AllowNull]&lt;/code&gt; attribute works for the setter only, it can be confusing for consumers of your class.&lt;/p&gt;

&lt;p&gt;To summarize, there are many solutions to making your JSON deserialization more reliable with C# nullable reference types. Whether you choose one of the approaches I have listed, or come up with another approach, don’t lie to the compiler, and don’t make life hard on yourself and your team by flowing potential &lt;code&gt;null&lt;/code&gt; references around unnecessarily.&lt;/p&gt;

&lt;p&gt;Remember that nullable warning suppressions are a code smell, and should be used with care - especially when you suppress warnings on values that are clearly &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be careful with Entity Framework
&lt;/h2&gt;

&lt;p&gt;For most frameworks and libraries, nullable reference types and annotations are just hints to the IDE and compiler. That is, until you encounter a framework that uses the annotations for other things, such as Entity Framework.&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://docs.microsoft.com/en-us/ef/core/modeling/entity-properties?tabs=data-annotations%2Cwithout-nrt#:~:text=A%20property%20is,as%20nullable%20columns."&gt;the documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A property is considered optional if it is valid for it to contain &lt;code&gt;null&lt;/code&gt;. If &lt;code&gt;null&lt;/code&gt; is not a valid value to be assigned to a property then it is considered to be a required property. When mapping to a relational database schema, required properties are created as non-nullable columns, and optional properties are created as nullable columns.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words: &lt;strong&gt;if you update the nullability of an entity in C# code, a migration will be created that changes nullability in the database as well&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;But what is considered “updating the nullability”? Adding or removing the nullable annotation (&lt;code&gt;?&lt;/code&gt;) is one such change, and merely adding &lt;code&gt;#nullable enable&lt;/code&gt; (or enabling nullability project-wide) is another.&lt;/p&gt;

&lt;p&gt;In other words: &lt;strong&gt;if you enable nullability in a project or file, your database may change&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types#required-and-optional-properties"&gt;Entity Framework documentation&lt;/a&gt; covers the various approaches to declaring entity properties and playing nice with C# nullable reference types.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this final post of our series, we have covered various techniques and tools that are available to migrate your existing projects to use C# nullable reference types.&lt;/p&gt;

&lt;p&gt;The benefit (and goal) of annotating your code, is that you get design-time and compile-time hints, from the IDE and the compiler. When adding annotations, steer clear of lying to the compiler. Suppressions are an anti-pattern in all but some cases. When needed, update the design of your API instead of passing around &lt;code&gt;null&lt;/code&gt; values without there being a need , other than making a warning go away.&lt;/p&gt;

&lt;p&gt;Thank you for reading!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;P.S.:&lt;/strong&gt; I realize this post was also a bit of a love letter for &lt;a href="https://www.jetbrains.com/resharper/"&gt;ReSharper (R#)&lt;/a&gt; and &lt;a href="https://www.jetbrains.com/rider"&gt;JetBrains Rider&lt;/a&gt;. Both tools are of great help when migrating and annotating your code.&lt;/p&gt;

&lt;p&gt;If you have migrated (or are in the process of migrating), what tools are you using? What help do they offer? I would love to hear about those in the comments!&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>nullability</category>
    </item>
    <item>
      <title>Annotating your C# code - Migrating to nullable reference types - Part 3</title>
      <dc:creator>Maarten Balliauw</dc:creator>
      <pubDate>Mon, 25 Apr 2022 02:44:05 +0000</pubDate>
      <link>https://dev.to/maartenba/annotating-your-c-code-migrating-to-nullable-reference-types-part-3-40k6</link>
      <guid>https://dev.to/maartenba/annotating-your-c-code-migrating-to-nullable-reference-types-part-3-40k6</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/maartenba/internals-of-c-nullable-reference-types-migrating-to-nullable-reference-types-part-2-gmd"&gt;previous post&lt;/a&gt;, we looked at some internals of C# nullable reference types, and the nullable annotation context.&lt;/p&gt;

&lt;p&gt;Today, let’s look at the many options for annotating your code and various ways to help the flow analysis understand your code. As a result, you (and anyone consuming your libraries) will get better and more reliable hints from the IDE and the C# compiler.&lt;/p&gt;

&lt;h2&gt;
  
  
  The need for more fine-grained annotations
&lt;/h2&gt;

&lt;p&gt;So far, we’ve only been annotating our code with &lt;code&gt;?&lt;/code&gt; to inform flow analysis that a reference type can be &lt;code&gt;null&lt;/code&gt; when nullable annotations are enabled. This one annotation may not be enough for all scenarios…&lt;/p&gt;

&lt;p&gt;Let’s start with a piece of code that takes a title (like the one from this blog post) and makes a “slug” out of it, a representation of the title that can be used in URLs. It’s a very, very naive implementation, so don’t go using it in production. But it’s sufficient as an example. Also note the &lt;code&gt;#nullable disable&lt;/code&gt; directive - the nullable annotation context is disabled in this example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="n"&gt;disable&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Slugify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&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="k"&gt;if&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;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="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="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Replace&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="s"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToLowerInvariant&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 &lt;code&gt;Slugify&lt;/code&gt; method can return a string (or &lt;code&gt;null&lt;/code&gt;), and accepts a string parameter that can also be a string instance, or &lt;code&gt;null&lt;/code&gt;. Let’s convert this method to use nullable reference types.&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="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;Slugify&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="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="k"&gt;if&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;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="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="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Replace&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="s"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToLowerInvariant&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, so good. The logic remained the same, and support for &lt;code&gt;null&lt;/code&gt; on both the input and output of this method remains.&lt;/p&gt;

&lt;p&gt;Now let’s use this method, pass it a known non-null string, and look at it in the IDE (or compile it).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Slugify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is a test"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slug&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;// Warning - CS8602 Dereference of a possibly null reference.&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We know that when we give the &lt;code&gt;Slugify()&lt;/code&gt; method a non-null value, a non-null string is returned. However, the compiler’s flow analysis does not know this and flags dereferencing &lt;code&gt;.Length&lt;/code&gt; as a warning. It uses the information available from the nullable context, which says both in- and output can be &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Luckily, there are several attributes available that can help us in cases where the compiler can’t figure things out. In this case, we can use the &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.notnullifnotnullattribute"&gt;&lt;code&gt;NotNullIfNotNullAttribute&lt;/code&gt;&lt;/a&gt; to give the compiler a hint. We can tell it the return value is not null, if a specific named argument is also not null:&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="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;NotNullIfNotNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"value"&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;Slugify&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="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="c1"&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;slug&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Slugify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is a test"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slug&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;// All good! Known to be not null&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s look at another example. After doing a &lt;code&gt;null&lt;/code&gt; check, the C# compiler knows that a specific reference can no longer be &lt;code&gt;null&lt;/code&gt;. A quick refresher:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;s&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="n"&gt;Day&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;null&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;s&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="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&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;// no warning here, s is known to be not null&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The C# compiler only knows that a &lt;code&gt;null&lt;/code&gt; check was performed if you write a &lt;code&gt;null&lt;/code&gt; check. Yet, it does seem to know &lt;code&gt;string.IsNullOrEmpty()&lt;/code&gt; is also doing such a check.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;s&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="n"&gt;Day&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;null&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&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="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&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;// no warning here, s is known to be not null&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How does this work? The &lt;code&gt;string.IsNullOrEmpty()&lt;/code&gt; method is annotated with the &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.notnullwhenattribute"&gt;&lt;code&gt;NotNullWhenAttribute&lt;/code&gt;&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;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;NotNullWhen&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="kt"&gt;string&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="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="k"&gt;value&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="m"&gt;0&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="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;)&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;false&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 &lt;code&gt;NotNullWhenAttribute&lt;/code&gt; lets the C# compiler know that the reference passed in the &lt;code&gt;value&lt;/code&gt; parameter is not &lt;code&gt;null&lt;/code&gt; when this method returns &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are quite a few of these attributes, with &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/attributes/nullable-analysis"&gt;good descriptions and examples in the docs&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preconditions
&lt;/h3&gt;

&lt;p&gt;Preconditions are used when writing to a parameter, field, or property setter.&lt;/p&gt;

&lt;p&gt;In the following example, the &lt;code&gt;Username&lt;/code&gt; property getter can not return &lt;code&gt;null&lt;/code&gt;, while the property setter does allow &lt;code&gt;null&lt;/code&gt;. The &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.allownullattribute"&gt;&lt;code&gt;AllowNull&lt;/code&gt;&lt;/a&gt; attribute lets you specify a parameter, field, or property setter accepts &lt;code&gt;null&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;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;_username&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="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="n"&gt;AllowNull&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Username&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;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_username&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;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_username&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="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="nf"&gt;ToString&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;Alternatively, &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.disallownullattribute"&gt;&lt;code&gt;DisallowNull&lt;/code&gt;&lt;/a&gt; lets you specify that a parameter, field, or property setter won’t accept null, even if it’s declared as nullable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Post-conditions
&lt;/h3&gt;

&lt;p&gt;Post-conditions are used when reading from a field, property, or return value.&lt;/p&gt;

&lt;p&gt;In the following example, the &lt;code&gt;MiddleName&lt;/code&gt; property getter may return &lt;code&gt;null&lt;/code&gt;, while the property setter requires a non-null value. The &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.maybenullattribute"&gt;&lt;code&gt;MaybeNull&lt;/code&gt;&lt;/a&gt; attribute lets you specify a field, property, or return value may be &lt;code&gt;null&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;private&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;_middleName&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MaybeNull&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;MiddleName&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;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_middleName&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;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_middleName&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="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.notnullattribute"&gt;&lt;code&gt;NotNull&lt;/code&gt;&lt;/a&gt; lets you specify that a field, property, or return value won’t return null, even if it’s declared as nullable.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;MaybeNull&lt;/code&gt; and &lt;code&gt;NotNull&lt;/code&gt; attributes are also useful in combination with generics, as we’ll see later in this post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conditional post-conditions
&lt;/h3&gt;

&lt;p&gt;Conditional post-conditions can be used to make method arguments dependent on a method return value.&lt;/p&gt;

&lt;p&gt;We’ve already seen &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.notnullwhenattribute"&gt;&lt;code&gt;NotNullWhen&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.notnullifnotnullattribute"&gt;&lt;code&gt;NotNullIfNotNull&lt;/code&gt;&lt;/a&gt; earlier, and there is also &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.maybenullwhenattribute"&gt;&lt;code&gt;MaybeNullWhen&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.maybenullwhenattribute"&gt;&lt;code&gt;MaybeNullWhen&lt;/code&gt;&lt;/a&gt; - A non-nullable argument may be null when the method returns the specified bool value.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.notnullwhenattribute"&gt;&lt;code&gt;NotNullWhen&lt;/code&gt;&lt;/a&gt; - A nullable argument won’t be null when the method returns the specified bool value.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.notnullifnotnullattribute"&gt;&lt;code&gt;NotNullIfNotNull&lt;/code&gt;&lt;/a&gt; - A return value isn’t null if the argument for the specified parameter isn’t null.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Helper methods
&lt;/h3&gt;

&lt;p&gt;With helper methods, you can specify whether a certain field is initialized after execution.&lt;/p&gt;

&lt;p&gt;In this &lt;code&gt;Person&lt;/code&gt; class, the &lt;code&gt;_id&lt;/code&gt; field initially is &lt;code&gt;null&lt;/code&gt;, even though it’s defined as being non-nullable. The &lt;code&gt;EnsureIdentifier()&lt;/code&gt; method is called in the constructor, and makes sure &lt;code&gt;_id&lt;/code&gt; does get a value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&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;public&lt;/span&gt; &lt;span class="nf"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;EnsureIdentifier&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;void&lt;/span&gt; &lt;span class="nf"&gt;EnsureIdentifier&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="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;However, when compiling, you’ll see a warning emitted:&lt;em&gt;CS8618 Non-nullable field ‘_id’ must contain a non-null value when exiting constructor. Consider declaring the field as nullable.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This can be resolved by adding the &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.membernotnullattribute"&gt;&lt;code&gt;MemberNotNull&lt;/code&gt;&lt;/a&gt; annotation on &lt;code&gt;EnsureIdentifier()&lt;/code&gt;, telling the compiler that this method sets the &lt;code&gt;_id&lt;/code&gt; field when it returns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&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;public&lt;/span&gt; &lt;span class="nf"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;EnsureIdentifier&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;MemberNotNull&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;_id&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;void&lt;/span&gt; &lt;span class="nf"&gt;EnsureIdentifier&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="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;&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.membernotnullattribute"&gt;&lt;code&gt;MemberNotNull&lt;/code&gt;&lt;/a&gt; lets you specify a member won’t be null when the method returns. There’s also &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.membernotnullwhenattribute"&gt;&lt;code&gt;MemberNotNullWhen&lt;/code&gt;&lt;/a&gt;, which lets you specify the listed member won’t be null when the method returns the specified &lt;code&gt;bool&lt;/code&gt; value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure conditions
&lt;/h3&gt;

&lt;p&gt;Lastly, there are two attributes that you can use to specify when no result will be returned, and further analysis is not needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.doesnotreturnattribute"&gt;&lt;code&gt;DoesNotReturn&lt;/code&gt;&lt;/a&gt; - The method or property always throws an exception, and thus never returns.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.doesnotreturnifattribute"&gt;&lt;code&gt;DoesNotReturnIf&lt;/code&gt;&lt;/a&gt; - If the associated &lt;code&gt;bool&lt;/code&gt; parameter has the specified value, the method or property always throws an exception, and thus never returns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, this &lt;code&gt;WriteName()&lt;/code&gt; method calls &lt;code&gt;ThrowArgumentNullException()&lt;/code&gt; when &lt;code&gt;name&lt;/code&gt; is null. Since that method is annotated with &lt;code&gt;DoesNotReturn&lt;/code&gt;, the compiler’s flow analysis now knows that the &lt;code&gt;Console.WriteLine&lt;/code&gt; below will only run when &lt;code&gt;name&lt;/code&gt; is not null.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DoesNotReturn&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;void&lt;/span&gt; &lt;span class="nf"&gt;ThrowArgumentNullException&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;argumentName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argumentName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;WriteName&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;name&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;name&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="nf"&gt;ThrowArgumentNullException&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;name&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="c1"&gt;// any code here will never execute&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&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="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In summary, by annotating your methods with these attributes, the C# compiler can provide you (and anyone who consumes your code) with more accurate warnings. And again, the &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/attributes/nullable-analysis"&gt;docs contain good examples&lt;/a&gt; on how to use these attributes.&lt;/p&gt;

&lt;h2&gt;
  
  
  JetBrains Annotations
&lt;/h2&gt;

&lt;p&gt;Introduced many years ago, &lt;a href="https://www.jetbrains.com/"&gt;JetBrains&lt;/a&gt; has &lt;a href="https://www.jetbrains.com/help/resharper/Reference__Code_Annotation_Attributes.html"&gt;many annotation attributes&lt;/a&gt; that can be used to provide hints to the IDE, even before C# 8 was a thing. If you’re using &lt;a href="https://jwww.jetbrains.com/resharper/"&gt;ReSharper&lt;/a&gt; or &lt;a href="https://jwww.jetbrains.com/rider/"&gt;JetBrains Rider&lt;/a&gt;, you can sprinkle these attributes through your code base and get better navigation, type hints, language injections, string formatting, and more.&lt;/p&gt;

&lt;p&gt;JetBrains Annotations have been around for years, and many existing code bases have them in use. For example, &lt;a href="https://www.hangfire.io/"&gt;Hangfire&lt;/a&gt; and &lt;a href="https://forum.unity.com/threads/embedded-jetbrains-annotations-in-v5-unityengine-dll.304819/#post-1990523"&gt;Unity&lt;/a&gt; are shipping their code with JetBrains Annotations, helping their users with information about nullability even before the compiler supported it.&lt;/p&gt;

&lt;p&gt;Here’s a summary of the JetBrains Annotations attributes related to nullability:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;What does it mean?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.jetbrains.com/help/resharper/Reference__Code_Annotation_Attributes.html#CanBeNullAttribute"&gt;&lt;code&gt;CanBeNull&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Indicates that the value of the marked element could be &lt;code&gt;null&lt;/code&gt; sometimes, so checking for &lt;code&gt;null&lt;/code&gt; is required before its usage.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.jetbrains.com/help/resharper/Reference__Code_Annotation_Attributes.html#NotNullAttribute"&gt;&lt;code&gt;NotNull&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Indicates that the value of the marked element can never be &lt;code&gt;null&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.jetbrains.com/help/resharper/Reference__Code_Annotation_Attributes.html#ItemNotNullAttribute"&gt;&lt;code&gt;ItemNotNull&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Can be applied to symbols of types derived from &lt;code&gt;IEnumerable&lt;/code&gt; as well as to symbols of &lt;code&gt;Task&lt;/code&gt; and &lt;code&gt;Lazy&lt;/code&gt; classes to indicate that the value of a collection item, of the &lt;code&gt;Task.Result&lt;/code&gt; property or of the &lt;code&gt;Lazy.Value&lt;/code&gt; property can never be &lt;code&gt;null&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.jetbrains.com/help/resharper/Reference__Code_Annotation_Attributes.html#ItemCanBeNullAttribute"&gt;&lt;code&gt;ItemCanBeNull&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Can be applied to symbols of types derived from &lt;code&gt;IEnumerable&lt;/code&gt; as well as to symbols of &lt;code&gt;Task&lt;/code&gt; and &lt;code&gt;Lazy&lt;/code&gt; classes to indicate that the value of a collection item, of the &lt;code&gt;Task.Result&lt;/code&gt; property or of the &lt;code&gt;Lazy.Value&lt;/code&gt; property can be &lt;code&gt;null&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.jetbrains.com/help/resharper/Contract_Annotations.html"&gt;&lt;code&gt;ContractAnnotation&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Let you define expected outputs for given inputs. For example, you can specify that when &lt;code&gt;null&lt;/code&gt; is passed as an argument, a method returns &lt;code&gt;null&lt;/code&gt;, and more.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;ContractAnnotation&lt;/code&gt; is really powerful: it provides a &lt;a href="https://www.jetbrains.com/help/resharper/Contract_Annotations.html#syntax"&gt;powerful syntax&lt;/a&gt; to describe the input and output conditions of a method.&lt;/p&gt;

&lt;p&gt;A simple example would be this &lt;code&gt;TryDeserialize&lt;/code&gt; method, where a &lt;code&gt;ContractAnnotation&lt;/code&gt; can be added to convey that when the &lt;code&gt;json&lt;/code&gt; parameter is &lt;code&gt;null&lt;/code&gt;, the method returns &lt;code&gt;false&lt;/code&gt; and &lt;code&gt;person&lt;/code&gt; will be &lt;code&gt;null&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="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ContractAnnotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"json:null =&amp;gt; false,person:null"&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;TryDeserialize&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;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&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 the &lt;code&gt;ContractAnnotation&lt;/code&gt; more clear than C#’s own attributes, as they follow the natural flow: if the input is &lt;code&gt;null&lt;/code&gt;, the output is &lt;code&gt;false&lt;/code&gt;. C#’s own attributes are a bit backward. For example &lt;code&gt;NotNullWhen&lt;/code&gt; tells the compiler that when the output is &lt;code&gt;false&lt;/code&gt;, the input must have been &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now you may ask, why am I mentioning these annotations? Especially with native annotations in C#8 and beyond? There are a number of reasons.&lt;/p&gt;

&lt;p&gt;First, teams that have been using these tools in the past will have an easier migration path towards using C#’s nullable annotations. The JetBrains tools have several automated quick fixes to handle this migration for you, as we’ll see in the next part of this series.&lt;/p&gt;

&lt;p&gt;Second, you may be consuming a library that ships JetBrains Annotations with it. If you’re using ReSharper or Rider, the IDE will use those annotations to help you determine whether reference types can be null (or not), even if you’re using the C# annotations.&lt;/p&gt;

&lt;p&gt;I won’t say they were first, but… JetBrains were first in paving the road towards better expressing nullability in C# code bases. You can clearly see the similarities between JetBrains flow based analysis and the solution for nullability the C# compiler settled on.&lt;/p&gt;

&lt;p&gt;Having annotated libraries out there is of great value when migrating your own code base. Do keep in mind that the C# compiler doesn’t recognize the JetBrains Annotations, but you can use them with Rider and ReSharper to get improved nullability analysis on top of any C# 8 annotations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; The JetBrains Annotations have several other utilities. For example, you can use them to &lt;a href="https://blog.jetbrains.com/dotnet/2018/05/02/improving-rider-resharper-code-analysis-using-jetbrains-annotations/"&gt;treat string parameters as ASP.NET MVC controllers/actions&lt;/a&gt;, and many more.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Expressing nullability with generics
&lt;/h2&gt;

&lt;p&gt;The nullable annotations can also be applied to generics. Here are a few examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;List&amp;lt;string&amp;gt;&lt;/code&gt; is a collection that contains non-null &lt;code&gt;string&lt;/code&gt; elements.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;List&amp;lt;string?&amp;gt;&lt;/code&gt; is a collection that may contain &lt;code&gt;null&lt;/code&gt; elements.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;List&amp;lt;string?&amp;gt;?&lt;/code&gt; is a collection that may be &lt;code&gt;null&lt;/code&gt;, and may contain &lt;code&gt;null&lt;/code&gt; elements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But what with &lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt;… Is &lt;code&gt;T&lt;/code&gt; nullable or not? Is it a value type or a reference type?&lt;/p&gt;

&lt;p&gt;You can use the &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.maybenullattribute"&gt;&lt;code&gt;MaybeNull&lt;/code&gt;&lt;/a&gt; attribute to tell the compiler about whether a generic method may return &lt;code&gt;null&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="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="n"&gt;MaybeNull&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;T&lt;/span&gt; &lt;span class="n"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... find by predicate, or return null ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters"&gt;generic type constraints&lt;/a&gt; to inform the compiler about the capabilities a type argument must have. With C# nullable reference types, there’s a new generic constraint to prevent a generic parameter from being nullable: &lt;code&gt;notnull&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to the &lt;code&gt;where T : notnull&lt;/code&gt; in the following example, &lt;code&gt;WriteToConsole()&lt;/code&gt; can only be called with a non-null &lt;code&gt;T item&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="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;WriteToConsole&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;notnull&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;s&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="nf"&gt;WriteToConsole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// CS8714 Nullability of type argument 'string?' doesn't match 'notnull' constraint.&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;notnull&lt;/code&gt; constraint limits the type parameter to be either a value type or a non-nullable reference type. Use the &lt;code&gt;class&lt;/code&gt; constraint to ensure a non-nullable reference type, or the &lt;code&gt;class?&lt;/code&gt; constraint to allow a nullable reference type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nullable reference types in referenced code, libraries and frameworks
&lt;/h2&gt;

&lt;p&gt;Migrating to using nullable reference types becomes easier when your dependencies have nullable annotations in place, whether the native C# attributes or using JetBrains Annotations. This is true for third-party dependencies, including the .NET framework, but it’s also true for first-party dependencies developed by your team or company.&lt;/p&gt;

&lt;p&gt;With .NET 6, most if not all of the framework code has been annotated, giving you design-time and compile-time help when interacting with framework code. Having annotations in the framework helps in annotating your own code base. In the following example, &lt;code&gt;Directory.GetParent()&lt;/code&gt; may return &lt;code&gt;null&lt;/code&gt;, which means your own function will need to also return a nullable &lt;code&gt;string&lt;/code&gt;, do a &lt;code&gt;null&lt;/code&gt; check, throw an &lt;code&gt;Exception&lt;/code&gt;, or work around this possibly null reference in another way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ljIcuIyb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/annotated-framework-code.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ljIcuIyb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/annotated-framework-code.png" alt="Annotated code in the framework" width="880" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once again, it’s important to realize nullable reference types and annotations are not enforced at runtime.&lt;/p&gt;

&lt;p&gt;Nullable reference types are a C#-only feature. The .NET Common Language Runtime (CLR) does not distinguish between &lt;code&gt;string&lt;/code&gt; and &lt;code&gt;string?&lt;/code&gt;, you don’t get runtime safety when it comes to nullability!&lt;/p&gt;

&lt;p&gt;Consumers of your code may also use an older C# language compiler or IDE, or have disabled nullable reference types and happily invoke your code that expects a non-nullable parameter with null.&lt;/p&gt;

&lt;p&gt;Because of all of this, you still need to do null checks, even with nullable reference types enabled.&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="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Reverse&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;original&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;original&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;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&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;original&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// or:&lt;/span&gt;
    &lt;span class="c1"&gt;// ArgumentNullException.ThrowIfNull(original);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;original&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="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing stops you from calling &lt;code&gt;Reverse(null)&lt;/code&gt;. To guard against &lt;code&gt;null&lt;/code&gt; at runtime, you’ll need to check for &lt;code&gt;null&lt;/code&gt; and throw &lt;code&gt;ArgumentNullException&lt;/code&gt; (or circumvent the &lt;code&gt;null&lt;/code&gt; value in another way).&lt;/p&gt;

&lt;p&gt;In summary, even with nullable reference types enabled, you still need to do &lt;code&gt;null&lt;/code&gt; checks on untrusted and/or external input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you need to redesign your API?
&lt;/h2&gt;

&lt;p&gt;An additional benefit to enabling nullable reference types, is that it may expose API design flaws.&lt;/p&gt;

&lt;p&gt;When you have to annotate an API as being nullable because there is one case where it can return &lt;code&gt;null&lt;/code&gt;, maybe you will want to re-think that design. Do you want to keep allowing &lt;code&gt;null&lt;/code&gt;? Do you want to instead make the API non-nullable, and handle that &lt;code&gt;null&lt;/code&gt; case in another way?&lt;/p&gt;

&lt;p&gt;Much like with &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;, nullable annotations will need to be present in your entire project to get the most value out of them. But also like with &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;, you may want to set boundaries on where certain values can be null or non-null, and redesign your API accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post, we have seen the many options for annotating your code for nullability. Using these annotations helps the compiler’s flow analysis in understanding your code. Thanks to annotated code, you and folks consuming your code get better and more reliable hints from the IDE and the C# compiler.&lt;/p&gt;

&lt;p&gt;In the next and final post in this series, we’ll look at some techniques and tools that are available to migrate an existing code base to using nullable reference types.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>nullability</category>
    </item>
    <item>
      <title>Internals of C# nullable reference types - Migrating to nullable reference types - Part 2</title>
      <dc:creator>Maarten Balliauw</dc:creator>
      <pubDate>Tue, 19 Apr 2022 02:44:05 +0000</pubDate>
      <link>https://dev.to/maartenba/internals-of-c-nullable-reference-types-migrating-to-nullable-reference-types-part-2-gmd</link>
      <guid>https://dev.to/maartenba/internals-of-c-nullable-reference-types-migrating-to-nullable-reference-types-part-2-gmd</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/maartenba/nullable-reference-types-in-c-migrating-to-nullable-reference-types-part-1-2k3o"&gt;previous post&lt;/a&gt;, we saw that with nullable reference types enabled, you get better static flow analysis when working on your code. While nullable reference types don’t give you runtime safety, the design-time and compile-time help is priceless!&lt;/p&gt;

&lt;p&gt;In this post, we’ll look at some internals of how C# nullable reference types work, and how the C# compiler and IDE use the nullable annotation context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Under the hood - Intermediate Language (IL)
&lt;/h2&gt;

&lt;p&gt;We’ve already seen there is a difference between nullability for value types and reference types.&lt;/p&gt;

&lt;p&gt;Value types are wrapped in a &lt;code&gt;Nullable&amp;lt;T&amp;gt;&lt;/code&gt; that gives you a &lt;code&gt;.HasValue&lt;/code&gt; property to determine if you can use the wrapped value, or should consider it as &lt;code&gt;null&lt;/code&gt;/no value.&lt;/p&gt;

&lt;p&gt;Reference types are always nullable, and with nullable reference types (NRT) enabled, you can provide extra context to the IDE and the compiler. With NRT enabled, the C# compiler treats reference types as non-nullable by default, and you can add syntax to annotate them as being nullable.&lt;/p&gt;

&lt;p&gt;Consider the following piece of code. Two methods, one returning a nullable integer, the other returning a nullable string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;GetInt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These two methods are compiled into the following Intermediate Language (IL):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;hidebysig&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;valuetype&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Nullable&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;GetInt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;cil&lt;/span&gt; &lt;span class="n"&gt;managed&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maxstack&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;

  &lt;span class="n"&gt;IL_0000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ldc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;IL_0001&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;newobj&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;valuetype&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Nullable&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;::.&lt;/span&gt;&lt;span class="nf"&gt;ctor&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;IL_0006&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;hidebysig&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="kt"&gt;string&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;cil&lt;/span&gt; &lt;span class="n"&gt;managed&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custom&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompilerServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NullableContextAttribute&lt;/span&gt;&lt;span class="p"&gt;::.&lt;/span&gt;&lt;span class="nf"&gt;ctor&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;unsigned&lt;/span&gt; &lt;span class="n"&gt;int8&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="m"&gt;01&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="m"&gt;02&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maxstack&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;

  &lt;span class="n"&gt;IL_0000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ldstr&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
  &lt;span class="n"&gt;IL_0005&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ret&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 break this down.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;int? GetInt() =&amp;gt; 1;&lt;/code&gt; is compiled into a &lt;code&gt;.method&lt;/code&gt; that returns a value type &lt;code&gt;[System.Runtime]System.Nullable&lt;/code&gt;1&lt;code&gt;. The method body pushes the value&lt;/code&gt;1&lt;code&gt;onto the stack, and creates a new&lt;/code&gt;System.Nullable&lt;code&gt;1&amp;lt;int32&amp;gt;&lt;/code&gt; that takes the first element from the stack (the &lt;code&gt;1&lt;/code&gt; that was just pushed). Finally, this new object is returned.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;string? GetString() =&amp;gt; "";&lt;/code&gt; is compiled into a &lt;code&gt;.method&lt;/code&gt; that returns a &lt;code&gt;string&lt;/code&gt;. The method body pushes a new object reference to a string literal stored in the assembly metadata (see &lt;a href="https://blog.maartenballiauw.be/post/2016/11/15/exploring-memory-allocation-and-strings.html"&gt;this post about string literals&lt;/a&gt; for more background), and returns it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference between value types and reference types is very clear in IL: the method that returns a value type, returns a &lt;code&gt;Nullable&amp;lt;int&amp;gt;&lt;/code&gt;. The method that returns a reference type returns, well, a reference type. There’s no trace of null vs. non-null!&lt;/p&gt;

&lt;p&gt;Or is there… In the IL code, &lt;code&gt;GetString()&lt;/code&gt; defines a &lt;code&gt;.custom&lt;/code&gt; attribute of type &lt;code&gt;NullableContextAttribute&lt;/code&gt;, passing the value &lt;code&gt;(01 00 02 00 00)&lt;/code&gt; to the attribute constructor. This is an 8-bit integer value (which exists in IL), and it translates to &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;NullableContextAttribute&lt;/code&gt; and &lt;code&gt;NullableAttribute&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://github.com/dotnet/roslyn/blob/main/docs/features/nullable-metadata.md#nullablecontextattribute"&gt;&lt;code&gt;NullableContextAttribute&lt;/code&gt;&lt;/a&gt; is used to provide code that consumes this method with some extra metadata, that can then be used by flow analysis.&lt;/p&gt;

&lt;p&gt;The C# compiler can add this attribute on types and methods, and supports 3 values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt; - Oblivious - Use the default, pre-C#8 behaviour (everything is maybe &lt;code&gt;null&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1&lt;/code&gt; - Not annotated - Consider the scope as not annotated by default (in other words, every reference type is non-nullable by default)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;2&lt;/code&gt; - Annotated - Consider the scope as annotated by default (in other words, every reference type has an implicit &lt;code&gt;?&lt;/code&gt; slapped onto it)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our previous example, the value of &lt;code&gt;2&lt;/code&gt; tells the flow analysis that the method return is annotated, in other words, it has a &lt;code&gt;?&lt;/code&gt; annotation.&lt;/p&gt;

&lt;p&gt;Let’s rewrite our &lt;code&gt;GetString()&lt;/code&gt; method to be non-nullable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&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 &lt;code&gt;NullableContextAttribute&lt;/code&gt; will now be created with a value of &lt;code&gt;1&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="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custom&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompilerServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NullableContextAttribute&lt;/span&gt;&lt;span class="p"&gt;::.&lt;/span&gt;&lt;span class="nf"&gt;ctor&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;unsigned&lt;/span&gt; &lt;span class="n"&gt;int8&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="m"&gt;01&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="m"&gt;01&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// int8(1)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In other words: we have a nullable context enabled, and by default everything in scope is considered to not be annotated (in other words, non-nullable).&lt;/p&gt;

&lt;p&gt;One more? What about two! Here are two new methods to explore. In C#:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetStringA&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;a&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;b&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;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;GetStringB&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;a&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;b&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;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&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 IL (just the signatures):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;hidebysig&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="nf"&gt;GetStringA&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;a&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;b&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;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;cil&lt;/span&gt; &lt;span class="n"&gt;managed&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;param&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="n"&gt;custom&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompilerServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NullableAttribute&lt;/span&gt;&lt;span class="p"&gt;::.&lt;/span&gt;&lt;span class="nf"&gt;ctor&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;unsigned&lt;/span&gt; &lt;span class="n"&gt;int8&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="m"&gt;01&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="m"&gt;02&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// int8(2)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custom&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompilerServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NullableAttribute&lt;/span&gt;&lt;span class="p"&gt;::.&lt;/span&gt;&lt;span class="nf"&gt;ctor&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;unsigned&lt;/span&gt; &lt;span class="n"&gt;int8&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="m"&gt;01&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="m"&gt;02&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// int8(2)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maxstack&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;

  &lt;span class="c1"&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;method&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;hidebysig&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="nf"&gt;GetStringB&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;a&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;b&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;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;cil&lt;/span&gt; &lt;span class="n"&gt;managed&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custom&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompilerServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NullableContextAttribute&lt;/span&gt;&lt;span class="p"&gt;::.&lt;/span&gt;&lt;span class="nf"&gt;ctor&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;unsigned&lt;/span&gt; &lt;span class="n"&gt;int8&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="m"&gt;01&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="m"&gt;02&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// int8(2)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custom&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompilerServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NullableAttribute&lt;/span&gt;&lt;span class="p"&gt;::.&lt;/span&gt;&lt;span class="nf"&gt;ctor&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;unsigned&lt;/span&gt; &lt;span class="n"&gt;int8&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="m"&gt;01&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="m"&gt;01&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="m"&gt;00&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// int8(1)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maxstack&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;

  &lt;span class="c1"&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 &lt;code&gt;GetStringA()&lt;/code&gt;, the compiler did not emit &lt;code&gt;NullableContextAttribute&lt;/code&gt;. Instead, it annotated the method parameters: &lt;code&gt;param [1]&lt;/code&gt; and &lt;code&gt;param [3]&lt;/code&gt; are annotated with &lt;code&gt;NullableAttribute&lt;/code&gt; and a value of &lt;code&gt;2&lt;/code&gt;. In other words, both parameters should be treated as being annotated (with a &lt;code&gt;?&lt;/code&gt;). By convention, the presence of a &lt;code&gt;NullableAttribute&lt;/code&gt; without &lt;code&gt;NullableContextAttribute&lt;/code&gt; also causes everything else to be treated as not annotated (in other words, non-nullable).&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;GetStringB()&lt;/code&gt;, the compiler emitted two attributes. By default, a &lt;code&gt;NullableContextAttribute&lt;/code&gt; with value &lt;code&gt;2&lt;/code&gt; is applied. In other words, every reference type in this method is annotated with a &lt;code&gt;?&lt;/code&gt;. Except for one: &lt;code&gt;param [2]&lt;/code&gt; (&lt;code&gt;string b&lt;/code&gt;) got a &lt;code&gt;NullableAttribute&lt;/code&gt; that has value &lt;code&gt;1&lt;/code&gt; (no annotation, so non-nullable).&lt;/p&gt;

&lt;p&gt;That’s some proper compiler magic going on right there! The C# compiler tries to emit as few attributes as possible, so that flow analysis in the IDE or in a consuming assembly does not have to process too much metadata: just a default nullable context, and any exceptions to that default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nullable annotation context
&lt;/h2&gt;

&lt;p&gt;So far, we’ve seen how the nullable context on a type or method changes the Intermediate Language (IL) that is emitted. This nullable context gives consuming code an idea of how to treat nullable reference types.&lt;/p&gt;

&lt;p&gt;The next step is telling flow analysis and the compiler what you want to do with that information, by setting the nullable annotation context. The nullable annotation context has 4 settings, and can be defined project-wide, or for every file separately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;disable&lt;/code&gt; - makes the compiler behave like it did pre-C# 8.0 (no NRT). You can not use &lt;code&gt;?&lt;/code&gt; on reference types.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;enable&lt;/code&gt; - enables null reference analysis and all language features.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;warnings&lt;/code&gt; - enables null reference analysis, and shows warnings when code might dereference &lt;code&gt;null&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;annotations&lt;/code&gt; - enables language features and lets you use &lt;code&gt;?&lt;/code&gt;, but does not enable null reference analysis.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Defining nullable annotation context project-wide
&lt;/h3&gt;

&lt;p&gt;To define the nullable annotation context project-wide, you can use the &lt;code&gt;&amp;lt;Nullable&amp;gt;...&amp;lt;/Nullable&amp;gt;&lt;/code&gt; property in your project file (or any MSBuild file that is included). Here’s an example of a project that sets the nullable annotation context to &lt;em&gt;enable&lt;/em&gt; for the entire project:&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;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class="nt"&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net6.0&lt;span class="nt"&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class="nt"&gt;&amp;lt;/Nullable&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;/Project&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Defining nullable annotation context per file
&lt;/h3&gt;

&lt;p&gt;You can also define the nullable annotation context in individual files, using the &lt;code&gt;#nullable&lt;/code&gt; preprocessor directive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;#nullable enable&lt;/code&gt; - enable nullable annotation context and nullable warning context&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#nullable disable&lt;/code&gt; - disable nullable annotation context and nullable warning context&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#nullable restore&lt;/code&gt; - set the nullable annotation context and nullable warning context to the project-level setting&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#nullable enable/disable/restore warnings&lt;/code&gt; - enable/disable/restore just the nullable warning context&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#nullable enable/disable/restore annotations&lt;/code&gt; - enable/disable/restore just the nullable annotation context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s also possible to use the &lt;code&gt;#nullable&lt;/code&gt; directive multiple times per file, and to enable/disable a specific context at various places in the same file. This may be useful when migrating large files to using nullable reference types, as you can split bits that you have already migrated from those you haven’t yet.&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="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="n"&gt;disable&lt;/span&gt;

&lt;span class="c1"&gt;// Warning: CS8632 - The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;SomeMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt;

&lt;span class="c1"&gt;// No warning - ? is allowed because nullable context is enabled&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;SomeOtherMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Which nullable annotation context should you use?
&lt;/h3&gt;

&lt;p&gt;Good question! As always, &lt;em&gt;“it depends”&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For new projects, it’s a good idea to fully enable the nullable annotation context at the project level. This way, you get the benefits of better static flow analysis from the first line of code you start writing.&lt;/p&gt;

&lt;p&gt;For existing projects and code bases, you’ll have to start with deciding what the default setting for your project will be. The end goal of migrating to nullable reference types, is to be able to have nullable warnings and annotations enabled in all projects. But that may not be the best default to start your migration with. Let’s look at some options.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;disable&lt;/code&gt; as the default
&lt;/h4&gt;

&lt;p&gt;One way to start migration, is by setting &lt;code&gt;disable&lt;/code&gt; as the default (or not adding &lt;code&gt;&amp;lt;Nullable /&amp;gt;&lt;/code&gt; to your project file at all). Doing so gives you the opportunity to go through your project and add &lt;code&gt;#nullable enable&lt;/code&gt; file by file, without drowning in warnings. When your entire project uses nullable reference types, you can switch to &lt;code&gt;enable&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;enable&lt;/code&gt; as the default
&lt;/h4&gt;

&lt;p&gt;Another way to start migration, is to go all-in and set &lt;code&gt;enable&lt;/code&gt; as the project default - this is the end goal so why not set it from the start? This option may (and most probably will) give you lots of warnings to work through. You’ll have to either add nullable annotations, add the null-suppressing operator to some statements, or add &lt;code&gt;#nullable disable&lt;/code&gt; for some files as you work your way through the code base.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;warnings&lt;/code&gt; as the default
&lt;/h4&gt;

&lt;p&gt;With &lt;code&gt;warnings&lt;/code&gt; as the project default, null reference analysis is enabled and warnings are shown when your code might dereference &lt;code&gt;null&lt;/code&gt;. You’ll see warnings where the compiler’s flow analysis infers potential &lt;code&gt;null&lt;/code&gt; references, and you can account for this. Either by adding &lt;code&gt;null&lt;/code&gt; checks, or by adding &lt;code&gt;#nullable enable&lt;/code&gt; to a file (or a section of a file) and applying &lt;code&gt;?&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;warnings&lt;/code&gt; context is a good way to start exploring an existing codebase and improve &lt;code&gt;null&lt;/code&gt; checks. It will make your project safer in terms of nullability, but unless you change the nullable annotation context, you’ll get no benefit of additional annotations in your code base.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; The &lt;code&gt;warnings&lt;/code&gt; context helps improve existing code bases with better flow analysis. To make sure you and your fellow developers address these warnings, you can treat these nullable warnings as errors. Set the following two properties in your project file:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;amp;lt;Nullable&amp;amp;gt;warnings&amp;amp;lt;/Nullable&amp;amp;gt;
&amp;amp;lt;WarningsAsErrors&amp;amp;gt;Nullable&amp;amp;lt;/WarningsAsErrors&amp;amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;annotations&lt;/code&gt; as the default
&lt;/h4&gt;

&lt;p&gt;When you set &lt;code&gt;annotations&lt;/code&gt; as the default, you can start using &lt;code&gt;?&lt;/code&gt; in your project. Flow analysis will be disabled, though, so you won’t get any warnings about potential nullability issues.&lt;/p&gt;

&lt;p&gt;In my opinion, this mode does not make a lot of sense. Yes, you can use the language features, but you get no assistance from the IDE or compiler when something is not right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post, we’ve seen some internals of C# nullable reference types. The compiler generates &lt;code&gt;NullableContextAttribute&lt;/code&gt; and &lt;code&gt;NullableAttribute&lt;/code&gt; usages when compiling C# code to Intermediate Language (IL), providing metadata about nullability.&lt;/p&gt;

&lt;p&gt;The metadata added by the compiler is used in various ways, depending on the nullable annotation context you specify in your project or separate files.&lt;/p&gt;

&lt;p&gt;In the next post, we’ll look beyond &lt;code&gt;?&lt;/code&gt; and cover the many options for annotating your code and helping out flow analysis to give you better and more reliable results.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>nullability</category>
    </item>
    <item>
      <title>Nullable reference types in C# - Migrating to nullable reference types - Part 1</title>
      <dc:creator>Maarten Balliauw</dc:creator>
      <pubDate>Mon, 11 Apr 2022 02:44:05 +0000</pubDate>
      <link>https://dev.to/maartenba/nullable-reference-types-in-c-migrating-to-nullable-reference-types-part-1-2k3o</link>
      <guid>https://dev.to/maartenba/nullable-reference-types-in-c-migrating-to-nullable-reference-types-part-1-2k3o</guid>
      <description>&lt;p&gt;The C# nullability features introduced in C#8 help you minimize the likelihood of encountering that dreaded &lt;code&gt;System.NullReferenceException&lt;/code&gt;. Nullability syntax and annotations give hints on whether a type can be nullable or not. Better static analysis is available to catch unhandled nulls while developing your code. What’s not to like?&lt;/p&gt;

&lt;p&gt;Introducing explicit nullability into an existing code bases is quite an effort. There’s much more to it than just sprinkling some &lt;code&gt;?&lt;/code&gt; and &lt;code&gt;!&lt;/code&gt; throughout your code. It’s not a silver bullet either: you’ll still need to check non-nullable variables for &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Throughout a series of blog posts, we’ll learn more about C# nullability, and cover some techniques and approaches that worked for me when migrating an existing codebase to using nullable reference types.&lt;/p&gt;

&lt;p&gt;Let’s start at the beginning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference types, value types, and null
&lt;/h2&gt;

&lt;p&gt;You may have heard of null being the “billion-dollar mistake”. In 2009, Tony Hoare, the creator of the ALGOL W programming language, &lt;a href="https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/"&gt;apologized for his billion-dollar mistake&lt;/a&gt;: null references. His original goal was to ensure that using any reference would be absolutely safe, validated with compiler checks. But supporting a null reference was easy to do, so he did - leading to innumerable errors, vulnerabilities, and system crashes in many systems out there. Other languages copied this idea, resulting in null references probably causing a billion dollars of pain and damage ever since.&lt;/p&gt;

&lt;p&gt;Nullable reference types have always been part of C#: a reference type can be either a reference, or null. Consider the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetValue&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;$"Length of '&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&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;s&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="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;s&lt;/code&gt; is not null, a message will be written to the console. But what happens when &lt;code&gt;s&lt;/code&gt; is null? Exactly: a &lt;code&gt;NullReferenceException&lt;/code&gt; is thrown when accessing the &lt;code&gt;Length&lt;/code&gt; property of… &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can add a check for &lt;code&gt;null&lt;/code&gt; before accessing this property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&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="s"&gt;$"Length of '&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&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;s&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="s"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"String is null."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How do you know whether you &lt;em&gt;need&lt;/em&gt; that check? If &lt;code&gt;GetValue()&lt;/code&gt; never returns &lt;code&gt;null&lt;/code&gt;, how would you know if you need to check &lt;code&gt;s&lt;/code&gt; when accessing its properties?&lt;/p&gt;

&lt;p&gt;For value types, such as &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt;, &lt;code&gt;decimal&lt;/code&gt;, &lt;code&gt;struct&lt;/code&gt;s like &lt;code&gt;DateTime&lt;/code&gt; or custom implementations, etc., you can make use of &lt;code&gt;Nullable&amp;lt;T&amp;gt;&lt;/code&gt; to make those value types “nullable”, and have a safe way of accessing them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; value types can’t &lt;em&gt;really&lt;/em&gt; be null - they are values, not references. With &lt;code&gt;Nullable&amp;lt;T&amp;gt;&lt;/code&gt;, you’re wrapping the value to be able to keep track of this additional &lt;code&gt;null&lt;/code&gt; state.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is some compiler magic that converts &lt;code&gt;int?&lt;/code&gt; into &lt;code&gt;Nullable&amp;lt;int&amp;gt;&lt;/code&gt;. When accessing a nullable value type, you know it will either be &lt;code&gt;null&lt;/code&gt; or contain a value, and that a check has to be made. With a nullable &lt;code&gt;DateTime&lt;/code&gt;, we could write the above example as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasValue&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;$"The date is: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&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;O&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="s"&gt;"No date was given."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key difference here is intent. With our &lt;code&gt;string&lt;/code&gt; before, you have no idea if &lt;code&gt;null&lt;/code&gt; is to be expected. It can never happen, it may happen - you only know by looking into the &lt;code&gt;GetValue()&lt;/code&gt; function (or take the safe approach and always add &lt;code&gt;null&lt;/code&gt; checks). With value types, the intent is more clear. A &lt;code&gt;DateTime&lt;/code&gt; can never be &lt;code&gt;null&lt;/code&gt;, whereas a &lt;code&gt;Nullable&amp;lt;DateTime&amp;gt;&lt;/code&gt; tells you that a check for &lt;code&gt;null&lt;/code&gt; (or &lt;code&gt;.HasValue&lt;/code&gt;) is required.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are nullable reference types?
&lt;/h2&gt;

&lt;p&gt;With C#8, &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references"&gt;nullable reference types (NRT)&lt;/a&gt; were introduced and help convey the intent that a reference can be &lt;code&gt;null&lt;/code&gt; or will never be &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s start by pointing out that reference types have always been nullable, as we saw in the introduction. They have been since the first version of C# - &lt;code&gt;string s = null&lt;/code&gt; is perfectly fine, and every reference type really always has been a nullable reference type.&lt;/p&gt;

&lt;p&gt;Why am I pointing this out? Reference types have been nullable forever, and now we’re flipping that idea by thinking of them as non-nullable by default, and adding syntax to annotate them as being nullable. The annotations help while writing and compiling your code, but don’t provide a runtime safety net. Let’s have a look at what that means.&lt;/p&gt;

&lt;p&gt;With nullable reference types enabled, the earlier example tells the compiler that we expect &lt;code&gt;s&lt;/code&gt; to not be &lt;code&gt;null&lt;/code&gt; (otherwise we’d declare &lt;code&gt;string? s&lt;/code&gt;), and we can safely remove the null check…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetValue&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;$"Length of '&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&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;s&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="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…or can we? Surprisingly, you can compile and run the above code, even with &lt;code&gt;GetValue()&lt;/code&gt; returning a &lt;code&gt;string&lt;/code&gt; that is &lt;code&gt;null&lt;/code&gt;. You’ll just see a &lt;code&gt;NullReferenceException&lt;/code&gt; is thrown when accessing the &lt;code&gt;Length&lt;/code&gt; property of… &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So what &lt;em&gt;are&lt;/em&gt; C#8 nullable reference types? I like to call them &lt;em&gt;nullable annotations&lt;/em&gt; - NRT’s annotate your code with what their nullability can be.&lt;/p&gt;

&lt;p&gt;When you compile the above code, or look at it in an IDE, you’ll see several warnings that mention you are doing something that may haunt you when you run the application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CS8600 - Converting null literal or possible null value to non-nullable type.&lt;/li&gt;
&lt;li&gt;CS8602 - Dereference of a possibly null reference.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YE-9bDbM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/rider-ide-nullable-reference-warning.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YE-9bDbM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/rider-ide-nullable-reference-warning.png" alt="Warnings when compiling code that is not null-safe" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks, IDE! Thanks, C# compiler! While this may all blow up at runtime, at least I get enough warnings to go fix this code. And that fixing can be done almost automatically, with a quick-fix.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZHThPnrh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/rider-ide-quickfix-make-nullable.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZHThPnrh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/rider-ide-quickfix-make-nullable.gif" alt="Quick-fix to change nullability of a reference type" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After applying these fixes, we’re essentially back at our initial example, but this time annotated with what we expect. Again, the IDE and compiler will help us out, and if we change that &lt;code&gt;GetValue()&lt;/code&gt; function to return a non-nullable &lt;code&gt;string&lt;/code&gt;, the tooling will tell us we’re doing redundant &lt;code&gt;null&lt;/code&gt; checks, and offer to remove them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetValue&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;$"Length of '&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&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;s&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="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Flow analysis
&lt;/h2&gt;

&lt;p&gt;There are some interesting things when it comes to nullability and flow analysis.&lt;/p&gt;

&lt;p&gt;For example, &lt;strong&gt;&lt;code&gt;var&lt;/code&gt; is always considered nullable&lt;/strong&gt;. The C# language team decided to always treat &lt;code&gt;var&lt;/code&gt; as nullable, and to rely on flow analysis to determine when the IDE or compiler should warn.&lt;/p&gt;

&lt;p&gt;In our example, the nullability of &lt;code&gt;s&lt;/code&gt; is determined by the annotations of the &lt;code&gt;GetValue()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetValue&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;$"Length of '&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&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;s&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="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Many IDEs will render inlay type hints to help you determine what a &lt;code&gt;var&lt;/code&gt; could be, based on flow analysis. Here’s a quick example. Note the inlay hint on &lt;code&gt;var s&lt;/code&gt; change when nullability of the &lt;code&gt;GetValue()&lt;/code&gt; function changes. Also note the squiggly at &lt;code&gt;s.Length&lt;/code&gt;, where flow analysis determines whether dereferencing &lt;code&gt;Length&lt;/code&gt; is potentially going to get you into trouble.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b4nyq_f9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/rider-ide-flow-analysis.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b4nyq_f9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://blog.maartenballiauw.be/images/2022/04/rider-ide-flow-analysis.gif" alt="Flow analysis" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cool, eh? Things become even cooler when you do &lt;code&gt;null&lt;/code&gt; checks. Here are two methods:&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;void&lt;/span&gt; &lt;span class="nf"&gt;MethodA&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="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;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="k"&gt;value&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;// CS8602 - Dereference of a possibly null reference.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;MethodB&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="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="k"&gt;if&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;null&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="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="k"&gt;value&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;// No warning, null check happened before&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;MethodA()&lt;/code&gt;, we’re not doing a &lt;code&gt;null&lt;/code&gt; check for the &lt;code&gt;value&lt;/code&gt; parameter, and flow analysis will warn about dereferencing a possibly null reference. In &lt;code&gt;MethodB()&lt;/code&gt;, flow analysis determined we &lt;em&gt;are&lt;/em&gt; doing a &lt;code&gt;null&lt;/code&gt; check, and is fine with accessing &lt;code&gt;.Length&lt;/code&gt; since there’s no chance of this line of code being hit with a &lt;code&gt;null&lt;/code&gt; reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  The null-forgiving operator
&lt;/h2&gt;

&lt;p&gt;Before wrapping up, there’s one more topic I want to get into. The null-forgiving operator (also known as the null-suppression operator, or the dammit-operator), &lt;code&gt;!&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When using nullable reference types, an expression can be postfixed with &lt;code&gt;!&lt;/code&gt; to tell the IDE and compiler’s static flow analysis to “ignore” the null state of the expression.&lt;/p&gt;

&lt;p&gt;Here are two examples, where &lt;code&gt;!&lt;/code&gt; is telling the flow analysis that a nullable state can be ignored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;a&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;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&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;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, this is not ideal - both of the above examples will throw &lt;code&gt;NullReferenceException&lt;/code&gt; when running the app.&lt;/p&gt;

&lt;p&gt;There are valid use cases, though. Flow analysis may not able to detect that a nullable value is actually non-nullable. You can suppress those cases, if you know a reference is not going to be null. In the following example, &lt;code&gt;IsValidUser()&lt;/code&gt; checks for null, so we can suppress the null warning we’d get otherwise in the &lt;code&gt;Console.WriteLine&lt;/code&gt; call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_userManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetUserById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;IsValidUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;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;$"Username: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;!.&lt;/span&gt;&lt;span class="n"&gt;Username&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;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsValidUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&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;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; there are better ways to solve some of these suppressions, using special attributes. I’ll cover these in the next post.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another case is when migrating a code base that does not yet use nullable reference types. You may want to (temporarily) suppress warnings on a certain code path, and not yet care about nullability while you are working on another part of the code base.&lt;/p&gt;

&lt;p&gt;Yet another case could be with unit tests. Maybe you do want to check what happens if someone passes &lt;code&gt;null&lt;/code&gt; into a function that expects a non-null reference. In those situations, you could pass &lt;code&gt;null!&lt;/code&gt; and see what happens.&lt;/p&gt;

&lt;p&gt;Do keep in mind the null-forgiving operator effectively disables flow analysis, and could easily hide nullability bugs in your code base. Use it with care, and consider the use of the null-suppressing operator a code smell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With nullable reference types enabled, you get improved static flow analysis on your code that helps determine if a variable may be null before dereferencing it. Variable annotations can be used to explicitly declare the intended null-state for that variable - adding &lt;code&gt;?&lt;/code&gt; to convey a &lt;code&gt;null&lt;/code&gt; reference is possible.&lt;/p&gt;

&lt;p&gt;Nullable reference types don’t give you runtime safety when it comes to nullability, but design-time and compile-time help is great to have!&lt;/p&gt;

&lt;p&gt;In the next post, we’ll look at some internals and the nullable annotation context.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>nullability</category>
    </item>
    <item>
      <title>A journey towards SpeakerTravel - Building a service from scratch</title>
      <dc:creator>Maarten Balliauw</dc:creator>
      <pubDate>Mon, 08 Nov 2021 02:44:05 +0000</pubDate>
      <link>https://dev.to/maartenba/a-journey-towards-speakertravel-building-a-service-from-scratch-57nh</link>
      <guid>https://dev.to/maartenba/a-journey-towards-speakertravel-building-a-service-from-scratch-57nh</guid>
      <description>&lt;p&gt;For close to two years now, I’ve had &lt;a href="https://speaker.travel/"&gt;SpeakerTravel&lt;/a&gt; up &amp;amp; running. It’s a tool that helps conference organizers to book flights for speakers. You invite speakers, they pick their flight of choice (within a budget the organizer can specify), and the organizer can then approve and book the flight with a single click.&lt;/p&gt;

&lt;p&gt;In this post, I want to go a bit into the process of building this tool. Why I started it in the first place, how it works, a look at it from the business side, and maybe a follow-up post that covers any questions you may have after reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I started building a travel booking tool
&lt;/h2&gt;

&lt;p&gt;Before COVID threw a wrench in offline activities, our &lt;a href="https://www.azug.be/"&gt;user group&lt;/a&gt; was organizing &lt;a href="https://www.cloudbrew.be/"&gt;CloudBrew&lt;/a&gt;, a 2-day conference with speakers from across the world (mostly Europe).&lt;/p&gt;

&lt;p&gt;Every year, I was complaining on Twitter around the time travel for those speakers needed to be booked. Booking flights for a speaker would mean several e-mails back-and-forth about the ideal schedule, checking travel budgets, and then sending the travel confirmation. And because our user group is a legal entity, we’d need invoices for our accountant, which meant contacting the travel agency and more e-mails.&lt;/p&gt;

&lt;p&gt;When we started, we did all of this for 5 speakers, which was doable. Then we grew, and in the end needed to do this for 19 speakers. Madness!&lt;/p&gt;

&lt;p&gt;That got me thinking, and almost pleading for someone to come up with a solution:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Startup idea: "Give travel site a bunch of e-mail addresses and budgets. Site lets those people select flights within that budget. I say yes/no and get billed." - Would love this for conference organizing!&lt;/p&gt;

&lt;p&gt;— Maarten Balliauw (@maartenballiauw) &lt;a href="https://twitter.com/maartenballiauw/status/1014515650902089731?ref_src=twsrc%5Etfw"&gt;July 4, 2018&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alas, by the time we had that 19 speaker booking coming up, no such solution came about, and we were once again doing the manual process.&lt;/p&gt;

&lt;h2&gt;
  
  
  How flight tickets work…
&lt;/h2&gt;

&lt;p&gt;In the back of my mind, the idea stuck. Would it be possible to build a solution to this problem, and make booking travel for speakers at our conference an easier task?&lt;/p&gt;

&lt;p&gt;Of course, building the app itself would be possible. It’s what we all do for a living! But what about the heart of this solution… You know, actually booking a flight ticket in an automated way?&lt;/p&gt;

&lt;p&gt;After researching and reading a lot, it seems that booking a flight ticket always consists of 4 steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You search an inventory of available seats for a flight combination;&lt;/li&gt;
&lt;li&gt;For that flight combination, a price is requested;&lt;/li&gt;
&lt;li&gt;For that flight combination, a booking is created;&lt;/li&gt;
&lt;li&gt;For that booking, tickets are issued.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Book flights via any website, and you’ll go through these steps. There’s a reason for this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The flight inventory is really a big database with all seats on all (or at least, many) airlines. As far as I could find, airlines populate this database a coule of times a year. It does not contain prices, just seats and conditions to book seats.&lt;/li&gt;
&lt;li&gt;Pricing checks a given seat with the airline (or other party in between). Requesting a price means the airline can give an actual price for a seat. They can also track interest in a specific seat/group of seats, and price accordingly.&lt;/li&gt;
&lt;li&gt;Booking reserves the seat, and removes that seat from the big flight inventory database. Ideally, booking has to happen soon after pricing. If no tickets have been issued after a couple of hours, the seat is made available again.&lt;/li&gt;
&lt;li&gt;Issuing tickets confirms the seat, and gives you the actual ticket that can be used to board a plane. Having these two steps separate means that in between, a booking website can ask you for payment, and only when that is confirmed, issue tickets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So in short, I needed something that could perform all of these steps somehow. More research!&lt;/p&gt;

&lt;h3&gt;
  
  
  Global Distribution Service (GDS)
&lt;/h3&gt;

&lt;p&gt;One of the first services that popped up were different Global Distribution Services (GDS) for air travel. The world has many of them. You may have heard of &lt;a href="https://www.amadeus.com/"&gt;Amadeus&lt;/a&gt;, &lt;a href="https://www.sabre.com/"&gt;Sabre&lt;/a&gt; or &lt;a href="https://www.travelport.com/"&gt;Travelport&lt;/a&gt;, but there are others.&lt;/p&gt;

&lt;p&gt;These GDS are an interoperability layer between inventories from airlines, travel agents, and more. They have software in place to handle interactions between all parties involved (airlines, travel agents, hotels, …), and until a few years ago, were always involved in booking flights. Nowadays, airlines often sell their inventory directly, without these middle-men involved.&lt;/p&gt;

&lt;p&gt;I explored various GDS’, and quickly found that this was not the way to go. First, they expect certain volumes of sales. I contacted one of them, and essentially got laughed at when I said I wanted to book around 20 flights a year. Second, from a technical point of view, a lot of them had documentation available that talked about XML-over-SOAP, WS-* standards, and all that. Been there, done that, but prefer the more lightweight integrations of recent years.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flight search affiliate programs
&lt;/h3&gt;

&lt;p&gt;There are a number of affiliate programs out there that provide an API that you can use to search flights (including an approximate price), and give you a link to the booking site. Examples are &lt;a href="https://www.travelpayouts.com/"&gt;Travelpayouts&lt;/a&gt; and &lt;a href="https://partners.skyscanner.net/affiliates/travel-apis"&gt;SkyScanner&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The conditions for using these APIs were somewhat restrictive for my use case, but e-mailing one of them confirmed this use case was something that could fit.&lt;/p&gt;

&lt;p&gt;Let the speaker search and request a flight, and then the organizer would click through and make the booking. This would still mean entering credit card details and invoice address a number of times, but it could work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Online Travel Agencies (OTA)
&lt;/h3&gt;

&lt;p&gt;Somewhere in-between GDS and affiliate programs, there are the Online Travel Agencies (OTA) and the likes. These companies are travel agents, and have their contracts with zero, one or several GDS, airlines, and more.&lt;/p&gt;

&lt;p&gt;Searching this space, I found a couple of them that had APIs available for the above 4 steps - which seemed promising as it would give full control over the booking process (including automation of sending the correct invoice details when purchasing a ticket):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.airhob.com/Developers"&gt;AirHob&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.kiwi.com/"&gt;Kiwi.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.travelopro.com/flight-api.php"&gt;Travelopro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.travelomatix.com/"&gt;Travelomatic&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After contacting them all, some responded only after a couple of weeks, others had requirements in terms of number of tickets sold (volume), and this got me disillusioned.&lt;/p&gt;

&lt;h3&gt;
  
  
  A travel agent from Sweden
&lt;/h3&gt;

&lt;p&gt;Having talked with a couple of folks about this idea and finding an API, a friend suggested I contact a travel agent they knew well as they could be able to help.&lt;/p&gt;

&lt;p&gt;We had a long call about the idea, and they were very helpful in providing some additional insights into the world of flight booking. They were using the TravelPort GDS themselves, and were building their own API on top of that to power their own websites. Unfortunately, they weren’t sure it would ever get completed, so this wasn’t a viable solution.&lt;/p&gt;

&lt;p&gt;Nevertheless, lesson learned: it never hurts to talk, even if it’s just for sharing insights and learnings.&lt;/p&gt;

&lt;h3&gt;
  
  
  AllMyles
&lt;/h3&gt;

&lt;p&gt;Some weeks after my disillusion with OTAs, I was searching the Internet once more and found another service: &lt;a href="https://www.allmyles.com/"&gt;AllMyles.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I decided to get in touch with some questions about my use case and low volumes. With zero expectations: I considered this my last attempt before shelving the entire idea.&lt;/p&gt;

&lt;p&gt;Responding in 3 days would have been a record, but these folks responded in 30 seconds (!). A good 10 minutes later I was on Skype with their founder. We chatted about the service I wanted to build, and he even gave some thoughts on how to implement certain parts and workflows.&lt;/p&gt;

&lt;p&gt;On &lt;a href="https://www.allmyles.com/"&gt;their website&lt;/a&gt;, a 30-day trial of their staging environment was promoted, and their founder confirmed this was flexible if needed. So I decided to go with this and experiment with the API to see what was possible and what was not, and maybe finally start building this application!&lt;/p&gt;

&lt;h2&gt;
  
  
  The business side…
&lt;/h2&gt;

&lt;p&gt;With the AllMyles API docs in hand, I set out to writing some code and experimenting with their staging environment. All seemed to work well for my use case.&lt;/p&gt;

&lt;p&gt;There was one thing in the way still… To get production access, a one-time certification fee of 3000 EUR would have to be paid. Definitely better than the volume requirements of other solutions, but still quite steep for booking 20 flights a year.&lt;/p&gt;

&lt;p&gt;What if this tool would be something that can be used by any conference out there, and I charge a small fee per passenger to cover this certification fee and other costs?&lt;/p&gt;

&lt;p&gt;Time to put on the business hat.&lt;/p&gt;

&lt;h3&gt;
  
  
  CENTS
&lt;/h3&gt;

&lt;p&gt;A couple of years ago, a friend recommended reading &lt;a href="https://amzn.to/302838r"&gt;The Millionaire Fastlane by MJ DeMarco&lt;/a&gt;. It’s a good book with ideas on getting out of the rat race that controls many of us, and very opinionated. You may or may not like this book. There’s one idea from the book that stuck in my head though: CENTS.&lt;/p&gt;

&lt;p&gt;CENTS is an acronym for the five aspects on which any idea can be vetted for viability as a business. It’s not a startup canvas or anything, just a simple way of checking if there is some viability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt; ontrol - Do you control as many elements of the business as possible, or would something like a price or policy change with a vendor mess with your business?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E&lt;/strong&gt; ntry - How hard is entering this market? Can anyone do it in 10 minutes, or would they need a lot of time, money, and other resources?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;N&lt;/strong&gt; eed - Does anyone actually need this thing you are thinking about?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;T&lt;/strong&gt; ime - Will you be converting time into money, or can you decouple the two and also earn while you’re asleep?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S&lt;/strong&gt; cale - Can you see this scale, are there pivots that would work, …&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before diving into the deep and coughing up that certification fee (and building the tool), I wanted to check these…&lt;/p&gt;

&lt;p&gt;For flight booking, &lt;strong&gt;C&lt;/strong&gt; ontrol is never going to be the case. Someone is flying the airplane, someone handles booking. There are parties in between you and that flight, and there’s no way around that. From my research, I knew if really needed I could find another OTA or GDS, and go with that, so I felt there was just enough control to give this aspect a green checkmark.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E&lt;/strong&gt; ntry was steep enough: that certification fee, research, building the app. Something everyone could overcome, but definitely not something everyone would do. As an added bonus, I had to figure out some tricks to find the same flight twice: once by the speaker making the search, once by the conference organizer to confirm booking. Pricing and booking have to be close together (as in, 20-30 minutes), but for SpeakerTravel there could even be a few days between both parties doing this. In any case, it requires some proper magic to get this right and fine the same (or a very comparable) seat. So &lt;strong&gt;E&lt;/strong&gt; ntry? Check!&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;N&lt;/strong&gt; eed aspect was easy. There are lots of conferences out there that are probably going through the same pain with booking flights. Check!&lt;/p&gt;

&lt;p&gt;Same with &lt;strong&gt;T&lt;/strong&gt; ime. This would be a software-as-a-service, that would allow folks to do self-service booking and payments, even when I’m not around. Check!&lt;/p&gt;

&lt;p&gt;Finally, &lt;strong&gt;S&lt;/strong&gt; cale. This solution could work for IT conferences, medical conferences, pretty much anything where a third party would pay for someone else’s flights. Business travel could be a pivot, where employees could book and employers would pay. Another pivot could be handling travel for music festivals, etc. So definitely not a hurdle in the long run!&lt;/p&gt;

&lt;p&gt;In short: it made &lt;del&gt;sense&lt;/del&gt; CENTS!&lt;/p&gt;

&lt;h3&gt;
  
  
  Legal requirements
&lt;/h3&gt;

&lt;p&gt;Building a tool for our own conference is one thing, building it for third-party use is another. Could I sell flight tickets from my Belgian company?&lt;/p&gt;

&lt;p&gt;Instead of trying to figure this out myself, I asked for advise here from a lawyer. The response came in (together with an invoice for their time researching), and for my Belgian company there were a few things to know about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flights-only is fine. You’re never &lt;em&gt;selling&lt;/em&gt; flights, you are facilitating a transaction between the traveler and the airline.&lt;/li&gt;
&lt;li&gt;If you combine flights and hotels, flights and rental cars, etc., you’re selling travel packages. Travel packages have stricter requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Great! So I could go ahead with flights (and only flights), and start building the app!&lt;/p&gt;

&lt;h3&gt;
  
  
  Payments
&lt;/h3&gt;

&lt;p&gt;While building the app (more on that later), I also was thinking about how to handle flight ticket payments… I’d have a fee per traveler (fixed), and the flight fare itself (variable, and one I’d have to pay directly to AllMyles).&lt;/p&gt;

&lt;p&gt;The two-step ticket issuing seemed like a perfect place to shove in a payment gateway, for example &lt;a href="https://www.stripe.com/"&gt;Stripe&lt;/a&gt;, and collect payment before making the actual booking through the API.&lt;/p&gt;

&lt;p&gt;Unfortunately, none of the payment gateways I found let you do “risky business”. All of them have different lists of business types that are not allowed, and travel is always on those lists. One payment gateway from The Netherlands confirmed they could support my scenario, but after requesting written confirmation that stance changed. In other words: credit cards were not an option.&lt;/p&gt;

&lt;p&gt;For now, I decided to go with an upfront deposit, to ensure flight fares can be paid when someone confirms their booking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building SpeakerTravel
&lt;/h2&gt;

&lt;p&gt;With a good idea in mind, and a blank canvas in front of me, it was time for the excitement of creating a new project in the IDE!&lt;/p&gt;

&lt;p&gt;The most important question: &lt;strong&gt;Which project template to start with?&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Attempt to a single-page application…
&lt;/h3&gt;

&lt;p&gt;Since I’d already built some API integration with AllMyles in C#, at least part of the application would probably be ASP.NET Core. With close to no experience with single page applications at the time, I thought this would be a good learning experience!&lt;/p&gt;

&lt;p&gt;So I went with an ASP.NET Core backend, &lt;a href="https://duendesoftware.com/"&gt;IdentityServer&lt;/a&gt;, and &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;About an hour of cursing on a simple “Hello, World” later, React was replaced with &lt;a href="https://vuejs.org/"&gt;Vue.js&lt;/a&gt; which seemed easier to get started with. I did have to replicate the &lt;a href="https://blog.maartenballiauw.be/post/2019/11/13/how-does-the-aspnetcore-spa-development-experience-work.html"&gt;ASP.NET Core SPA development experience (blog post)&lt;/a&gt; to support Vue.js, but that was fun to do and write about.&lt;/p&gt;

&lt;p&gt;What wasn’t fun though, was the slow-going. New to Vue.js, a lot of things went very slow while building. After 2 weeks of spending evenings on just a login that worked smoothly, I started wondering…&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Am I doing this to solve a problem, or to learn new tech?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Building this thing over the weekend and in the evening hours, I reconsidered the tech stack and started anew.&lt;/p&gt;

&lt;h3&gt;
  
  
  …replaced with boring technology
&lt;/h3&gt;

&lt;p&gt;This time, I started with an ASP.NET MVC Core project. Individual user accounts using ASP.NET Core Identity, Entity Framework, and SQL Server. A familiar stack for me, and a stack in which I was immediately productive.&lt;/p&gt;

&lt;p&gt;A few hours into development, I had the login/register/manage account pages customized. The layout page was converted to load a &lt;a href="https://bootswatch.com/"&gt;Bootswatch UI theme&lt;/a&gt; (on top of &lt;a href="https://getbootstrap.com/"&gt;Bootstrap&lt;/a&gt;), and was starting to get into building the flows of inviting speakers, searching flights (with 100% made up data), approving and rejecting flights, and all that. This was finished in a week or 6 and then another few weeks to properly integrate with AllMyles’ staging environment.&lt;/p&gt;

&lt;p&gt;While developing the app, a lot of new ideas and improvements popped up. I tried to be ruthless in asking myself &lt;em&gt;“do I really need this for version 1?”&lt;/em&gt;, and log anything else in the issue tracker and pick it up in the future. This definitely helped with productivity.&lt;/p&gt;

&lt;p&gt;Some fun was had implementing &lt;a href="https://blog.maartenballiauw.be/post/2020/04/14/building-an-aspnet-core-tag-helper-to-show-hide-ui-elements-based-on-authorization.html"&gt;tag helpers to show/hide HTML elements (blog post)&lt;/a&gt;, which realy works well to make certain parts of the UI available based on user permissions and roles.&lt;/p&gt;

&lt;p&gt;The first version was ready near the end of August 2019, including a basic &lt;a href="https://speaker,travel/"&gt;product website&lt;/a&gt; that is powered by a very simple Markdown-to-HTML script that seems to work well.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://app.speaker.travel/"&gt;application itself&lt;/a&gt; was built and deployed on the following stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ASP.NET Core MVC + Razor pages for the scaffolded identity area, on .NET Core 2.1&lt;/li&gt;
&lt;li&gt;Bootstrap and Bootswatch for UI&lt;/li&gt;
&lt;li&gt;A sprinkle of jQuery&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.hangfire.io/"&gt;Hangfire&lt;/a&gt; for background jobs (the actual bookingm, sending e-mails, anything that’s async/retryable)&lt;/li&gt;
&lt;li&gt;SQL Server LocalDb for development, Azure SQL Database for production&lt;/li&gt;
&lt;li&gt;Azure Web Apps for the app and product website&lt;/li&gt;
&lt;li&gt;Private GitHub repository&lt;/li&gt;
&lt;li&gt;Azure DevOps to build and deploy&lt;/li&gt;
&lt;li&gt;SendGrid for sending e-mails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, this was and is a very familiar stack for me, and as a result a stack in which I was immediately productive. Server-side rendering is fine 😉 And .NET is truly great!&lt;/p&gt;

&lt;p&gt;When you have an idea you want to build out, I can highly recommend going with what you know - unless of course the goal is exploring another tech.&lt;/p&gt;

&lt;h3&gt;
  
  
  The domain model
&lt;/h3&gt;

&lt;p&gt;When I asked folks on Twitter for what they wanted to see in this post, &lt;a href="https://twitter.com/cezary_piatek"&gt;Cezary Piatek&lt;/a&gt; wanted to know about the domain model.&lt;/p&gt;

&lt;p&gt;From a high-level, the domain model of this application is simple. There’s an &lt;code&gt;Event&lt;/code&gt; that has &lt;code&gt;Traveler&lt;/code&gt;s, and at some point, they will have a &lt;code&gt;Booking&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For every traveler, the system keeps a &lt;code&gt;TravelerStatus&lt;/code&gt; history, which represents state transitions. From &lt;code&gt;invited&lt;/code&gt;, to &lt;code&gt;accepted&lt;/code&gt;, to &lt;code&gt;bookingrequested&lt;/code&gt;, to &lt;code&gt;confirmed&lt;/code&gt;/&lt;code&gt;rejected&lt;/code&gt;/&lt;code&gt;canceled&lt;/code&gt;, to &lt;code&gt;ticketsissued&lt;/code&gt;, and potentially back to the start where a &lt;code&gt;rejected&lt;/code&gt; traveler goes to &lt;code&gt;accepted&lt;/code&gt; again so they can make a new search.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;TravelerStatus&lt;/code&gt; history is evaluated for every traveler, and the system takes these into account. In fact, they are somewhat visible in the application UI as well (though some of these state transitions are combined for UX purposes).&lt;/p&gt;

&lt;p&gt;When a &lt;code&gt;Traveler&lt;/code&gt; requests a booking, some PII is stored. Passenger name, birth date, and whatever the airline requires to book a given seat. This data is stored as a JSON blob - the fields are dynamic and may differ depending on the airline. This data is always destroyed after tickets are issues, the booking request was rejected, or when the booking was still waiting for approval but the event has concluded 10 days ago.&lt;/p&gt;

&lt;p&gt;For flight search and booking, the domain model is a 1:1 copy of what AllMyles has in their API. Looking at other APIs, it’s roughly the standard model in the world of flights. A &lt;code&gt;Search&lt;/code&gt; returns one or more &lt;code&gt;SearchResult&lt;/code&gt;s. Each of those has one or more &lt;code&gt;Combination&lt;/code&gt;s, typically flights that have the same conditions and price, but different times. E.g. a shuttle flight from Brussels to Frankfurt may return 3 combinations here - same price, and conditions, just 3 different times during the day. A &lt;code&gt;Combination&lt;/code&gt; can also have upgrade and baggage option. The booking itself is essentially makign a call that passes a given &lt;code&gt;Combination&lt;/code&gt; identifier (and what options are selected on top).&lt;/p&gt;

&lt;h3&gt;
  
  
  Ready for take-off!
&lt;/h3&gt;

&lt;p&gt;The app was deployed (targeting AllMyles staging), and I requested certification (coughing up the initial fee - no turning back now!). This process took a couple of days, but at some point I was given production access and SpeakerTravel was live!&lt;/p&gt;

&lt;p&gt;This was right on time for our CloudBrew conference in 2019, and it was really exciting to see folks request flights, booking them via the API, and seeing actual flight tickets sent out by airlines. Not to mention, much easier in terms of workload and back-and-forth compared to the manual process that triggered this entire endeavour! And speakers themselves also enjoyed this workflow:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Massive props to &lt;a href="https://twitter.com/CloudBrewConf?ref_src=twsrc%5Etfw"&gt;@CloudBrewConf&lt;/a&gt; - their travel booking system for the speakers has really raised the bar!&lt;/p&gt;

&lt;p&gt;— Paul Stack (@stack72) &lt;a href="https://twitter.com/stack72/status/1161543315218739201?ref_src=twsrc%5Etfw"&gt;August 14, 2019&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thanks, Paul 🤗&lt;/p&gt;

&lt;p&gt;Very quickly, a couple of organizer-friends jumped aboard as well. And for a conference I was attending myself, I used it to book a flight in my own name. Pretty cool!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;First time taking a flight booked through my own &lt;a href="https://twitter.com/SpeakerTravel_?ref_src=twsrc%5Etfw"&gt;@SpeakerTravel_&lt;/a&gt; - pretty cool to fly on a ticket you issued yourself 😎&lt;/p&gt;

&lt;p&gt;— Maarten Balliauw (@maartenballiauw) &lt;a href="https://twitter.com/maartenballiauw/status/1217352024091766785?ref_src=twsrc%5Etfw"&gt;January 15, 2020&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A couple of conferences later, some bugs were ironed out, some feature requests were handled, and the certification fee was covered. Business-wise, and conveniently brushing aside time spent building this thing, SpeakerTravel was break-even!&lt;/p&gt;

&lt;h3&gt;
  
  
  COVID-19 💊 and working on the backlog
&lt;/h3&gt;

&lt;p&gt;And then, half a year after release, a pandemic hit the world. Conferences all went online, travel virtually halted, and no new conferences onboarded SpeakerTravel for a long time.&lt;/p&gt;

&lt;p&gt;This was a bummer, but a good time to work on that backlog of features I wanted to add. Some technical debt got fixed, and thanks to fast release cadences in both the front-end and .NET world, I’ve been upgrading a lot of things, many times.&lt;/p&gt;

&lt;p&gt;Today’s tech stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ASP.NET Core MVC + Razor pages for the scaffolded identity area, on .NET 6.0 RC2&lt;/li&gt;
&lt;li&gt;Bootstrap and Bootswatch&lt;/li&gt;
&lt;li&gt;A sprinkle of jQuery (that I want to replace with &lt;a href="https://htmx.org/"&gt;HTMX&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.hangfire.io/"&gt;Hangfire&lt;/a&gt; for background jobs (the actual bookingm, sending e-mails, anything that’s async/retryable)&lt;/li&gt;
&lt;li&gt;SQL Server LocalDb for development, Azure SQL Database for production&lt;/li&gt;
&lt;li&gt;Product website and application are Docker images now, deployed to Azure Web Apps for Containers&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.jetbrains.com/space/"&gt;JetBrains Space&lt;/a&gt; for Git, CI/CD, and container registry&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.mailjet.com/"&gt;Mailjet&lt;/a&gt; for sending out e-mails. Smaller company, better support.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you’re interested in seeing CI/CD with Space, check &lt;a href="https://twitter.com/maartenballiauw/status/1440228410668716035/"&gt;this Twitter thread&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;Good question! I think this question can be split as well…&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next on the technical side?
&lt;/h2&gt;

&lt;p&gt;Let’s start with this one. As in the past months, working on some items from the backlog and just keeping things up to date. Very high on my wishlist is ripping out jQuery and replacing the few bits that require client-side interactivity with &lt;a href="https://htmx.org/"&gt;HTMX&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of the things I do want to try at some point is seeing if I can run the entire stack on Kubernetes, but that’s purely out of personal interest.&lt;/p&gt;

&lt;p&gt;Any other nerd snipes are welcome in the comments!&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next on the business side?
&lt;/h2&gt;

&lt;p&gt;What’s immediately next, is definitely uncertainty. We’re still in a pandemic, and while parts of the world seem to be evolving into the right direction for SpeakerTravel, it’s unsure when in-person conferences will pick up again.&lt;/p&gt;

&lt;p&gt;Apart from infrastructure, there’s no real cost to running the application, so I can be patient on that side and keep pitching it to anyone I meet, and provide good support for those who do sign up in the meanwhile.&lt;/p&gt;

&lt;p&gt;Speaking of which, I’m super happy that since September 2021, a few conferences have been using the product for in-person travel!&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not pivot?
&lt;/h3&gt;

&lt;p&gt;A question I got recently was: &lt;em&gt;Why not pivot to business travel?&lt;/em&gt; - great idea!&lt;/p&gt;

&lt;p&gt;Earlier in this post, I described the model where employees could search and pick travel options, and the company can approve and pay. This would indeed be a great pivot, but there are a couple of things holding me back on this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s a very crowded market (with some big players like American Express). This is not a big issue though, it validates there is a market, but it would need quite some effort to get traction.&lt;/li&gt;
&lt;li&gt;I’d have to expand from flights into flights + hotels + cars. While possible in terms of APIs, it does require fulfilling some extra regulations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both of these would mean going bigger than what I currently want to handle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and Takeaways
&lt;/h2&gt;

&lt;p&gt;Sometimes, you have a story in you that you just want to write down. This was one of those.&lt;/p&gt;

&lt;p&gt;Instead of sharing the event of having &lt;a href="https://speaker.travel/"&gt;SpeakerTravel&lt;/a&gt; online, I wanted to share the story about the process that brought it about. Maybe we all focus on the event too much, and not enough on the process towards the event.&lt;/p&gt;

&lt;p&gt;Social media consists of short bits, while blogs, articles and tutorials about the process have so much value. Leave breadcrumbs for those on a similar path like you in the future.&lt;/p&gt;

&lt;p&gt;Speaking of that: if there’s anything in this blog post you would like to see a follow-up on with more details, let me know via the comments.&lt;/p&gt;

&lt;p&gt;Take care!&lt;/p&gt;

</description>
      <category>travel</category>
      <category>conferences</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
