<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Dotnet on ESG's</title><link>https://esg.dev/tags/dotnet/</link><description>Recent content in Dotnet on ESG's</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Tue, 28 Mar 2023 19:15:26 -0400</lastBuildDate><atom:link href="https://esg.dev/tags/dotnet/index.xml" rel="self" type="application/rss+xml"/><item><title>Using Cloud SQL IAM Authentication with PostgreSQL on .NET</title><link>https://esg.dev/posts/using-gcp-iam-with-pgsql/</link><pubDate>Tue, 28 Mar 2023 19:15:26 -0400</pubDate><guid>https://esg.dev/posts/using-gcp-iam-with-pgsql/</guid><description>&lt;p&gt;Google Cloud Platform supports &lt;a href="https://cloud.google.com/sql/docs/postgres/authentication#automatic"&gt;automatic IAM authentication for databases&lt;/a&gt;, but .NET is not officially supported.&lt;/p&gt;
&lt;p&gt;Thankfully, &lt;a href="https://www.npgsql.org/"&gt;Npgsql&lt;/a&gt;, a ADO.NET Data Provider for PostgreSQL, has enough features to make it easy!&lt;/p&gt;
&lt;h1 id="create-and-configure-a-service-account"&gt;Create and configure a service account&lt;/h1&gt;
&lt;p&gt;First, &lt;a href="https://cloud.google.com/iam/docs/service-accounts-create"&gt;create a service account&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Then, assign it the &lt;code&gt;roles/cloudsql.instanceUser&lt;/code&gt; on your Cloud SQL instance. This can be also be done at the project level, but it&amp;rsquo;s generally better to keep your scopes tighter.&lt;/p&gt;
&lt;p&gt;Lastly, add the &lt;a href="https://cloud.google.com/sql/docs/postgres/add-manage-iam-users#creating-a-database-user"&gt;service account as an IAM user&lt;/a&gt; to your database instance&lt;/p&gt;
&lt;p&gt;Note: This would also work with a user account, but we&amp;rsquo;ll be using service accounts to keep things simple&lt;/p&gt;
&lt;p&gt;Once all of this is done, your service account is ready to go&lt;/p&gt;
&lt;h1 id="write-the-code"&gt;Write the code&lt;/h1&gt;
&lt;p&gt;We&amp;rsquo;ll be using a &lt;code&gt;NpgsqlDataSourceBuilder&lt;/code&gt;, with the &lt;code&gt;UsePeriodicPasswordProvider&lt;/code&gt; property in order to refresh our GCP access token.&lt;/p&gt;
&lt;p&gt;You will need to install the following Nuget packages in your project:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/Google.Apis.Auth/"&gt;Google.Apis.Auth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/Npgsql"&gt;Npgsql&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We first start by readying our &lt;code&gt;GoogleCredential&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// You could load this from a key file, or anywhere else, but this should cover most cases.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; credentials = &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; GoogleCredential.GetApplicationDefaultAsync();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// We want to limit the scope of the token, so we create scoped credentials &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; scopedCredentials = credentials.CreateScoped(&lt;span style="color:#e6db74"&gt;&amp;#34;https://www.googleapis.com/auth/sqlservice.login&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, we need to setup a &lt;code&gt;NpgsqlDataSource&lt;/code&gt; using &lt;code&gt;NpgsqlDataSourceBuilder&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Conveniently enough, Google auth will take care of refreshing the token on demand. If the token is not expired, &lt;code&gt;GetAccessTokenForRequestAsync&lt;/code&gt; will return a cached version. This means that we can keep &lt;code&gt;successRefreshInterval&lt;/code&gt; and &lt;code&gt;failureRefreshInterval&lt;/code&gt; pretty short (the latter should never happen). In this case, we use 1 minute and 0 minutes respectively.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; dataSourceBuilder = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; NpgsqlDataSourceBuilder();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dataSourceBuilder.UsePeriodicPasswordProvider(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (settings, cancellationToken) =&amp;gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; ValueTask&amp;lt;&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;&amp;gt;(scopedCredentials.UnderlyingCredential.GetAccessTokenForRequestAsync(cancellationToken: cancellationToken)),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TimeSpan.FromMinutes(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TimeSpan.FromSeconds(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dataSourceBuilder.ConnectionStringBuilder.Database = &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;lt;your database name&amp;gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dataSourceBuilder.ConnectionStringBuilder.Username = &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;lt;Your iam user. ex: name@project.iam&amp;gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dataSourceBuilder.ConnectionStringBuilder.Host = &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;lt;The IP address of your database&amp;gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; dataSource = dataSourceBuilder.Build();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All that&amp;rsquo;s left is to create the connection and query the database.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;using&lt;/span&gt; var connection = dataSource.CreateConnection();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; connection.OpenAsync();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;using&lt;/span&gt; var cmd = connection.CreateCommand();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cmd.CommandText = &lt;span style="color:#e6db74"&gt;&amp;#34;SELECT 1&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; result = cmd.ExecuteScalarAsync();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Console.WriteLine(result);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that&amp;rsquo;s it, you now have secure, password-less access to your PostgreSQL server.&lt;/p&gt;
&lt;h1 id="what-about-entity-framework"&gt;What about Entity Framework?&lt;/h1&gt;
&lt;p&gt;Good question! It turns out that &lt;a href="https://www.nuget.org/packages/Npgsql.EntityFrameworkCore.PostgreSQL/"&gt;Npgsql.EntityFrameworkCore.PostgreSQL&lt;/a&gt; supports passing in a &lt;code&gt;DataSource&lt;/code&gt; out of the box.&lt;/p&gt;
&lt;p&gt;In an ASP.NET setting, it would look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;builder.Services.AddDbContext&amp;lt;Context&amp;gt;(options =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; options.UseNpgsql(dataSource);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Want to register your DataSource for dependency injection? Sure!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;builder.Services.AddSingleton(dataSource);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;builder.Services.AddDbContext&amp;lt;Context&amp;gt;((serviceProvider, options) =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; dataSource = serviceProvider.GetRequiredService&amp;lt;NpgsqlDataSource&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; options.UseNpgsql(dataSource);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that&amp;rsquo;s all there is to it.&lt;/p&gt;</description></item><item><title>Deferred processing in ASP.NET Core 6</title><link>https://esg.dev/posts/deferred-processing-in-aspnetcore/</link><pubDate>Sun, 17 Jul 2022 18:01:55 -0400</pubDate><guid>https://esg.dev/posts/deferred-processing-in-aspnetcore/</guid><description>&lt;p&gt;Nowadays, a common pattern we see when treating HTTP requests is to do a minimal amount of work before returning a response, while treating longer operations (ex: sending email) in the background after the request has been completed.&lt;/p&gt;
&lt;p&gt;Unfortunately, in ASP.NET, we often see this being done in questionable ways, mainly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Not using &lt;code&gt;await&lt;/code&gt; on &lt;code&gt;Task&lt;/code&gt; object&lt;/li&gt;
&lt;li&gt;Wrapping some code in &lt;code&gt;Task.Run&lt;/code&gt; and not awaiting the results.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This can lead to dependency injection issues (if your request ends, all of our scoped objected are gone), or having exceptions thrown into the wind.&lt;/p&gt;
&lt;p&gt;Fortunately, .NET has it&amp;rsquo;s own internal publisher/subscriber-like feature, called Channels.&lt;/p&gt;
&lt;h1 id="channels"&gt;Channels&lt;/h1&gt;
&lt;p&gt;Channels were first introducted in .NET Core 3.0. While they&amp;rsquo;ve had a few improvements since, the abstract classes were mostly unchanged.&lt;/p&gt;
&lt;h2 id="the-basics"&gt;The basics&lt;/h2&gt;
&lt;p&gt;A lot has been written about how to usage channels, so I won&amp;rsquo;t go in too deep here.&lt;/p&gt;
&lt;p&gt;First, let&amp;rsquo;s go over the building blocks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Channel&lt;/code&gt;: Static class used to create channels&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Channel&amp;lt;T&amp;gt;&lt;/code&gt;: Abstract class representing a channel&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ChannelReader&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code&gt;ChannelWriter&amp;lt;T&amp;gt;&lt;/code&gt;: Abstract classes representing readers and writers to a channel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A simple setup would be:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; channel = Channel.CreateUnbounded&amp;lt;Model&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; reader = channel.Reader;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; writer = channel.Writer;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="integration-with-aspnet-core"&gt;Integration with ASP.NET Core&lt;/h1&gt;
&lt;p&gt;We&amp;rsquo;ll be assuming that we want to process a model named, appropriately, &lt;code&gt;Model&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="background-service"&gt;Background service&lt;/h2&gt;
&lt;p&gt;First, let&amp;rsquo;s created a &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0&amp;amp;tabs=visual-studio"&gt;HostedService&lt;/a&gt; to receive the messages.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll be inheriting from &lt;code&gt;BackgroundService&lt;/code&gt; since it makes the implementation a little simpler.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ModelService&lt;/span&gt; : BackgroundService
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;readonly&lt;/span&gt; ChannelReader&amp;lt;Model&amp;gt; _channelReader;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;readonly&lt;/span&gt; ILogger&amp;lt;Service&amp;gt; _logger;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; ModelService(ChannelReader&amp;lt;Model&amp;gt; channelReader, ILogger&amp;lt;Service&amp;gt; logger)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _channelReader = channelReader;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _logger = logger;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;protected&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;override&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; Task ExecuteAsync(CancellationToken stoppingToken)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; _channelReader.WaitToReadAsync(stoppingToken))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (_channelReader.TryRead(&lt;span style="color:#66d9ef"&gt;out&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; item))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _logger.LogInformation(&lt;span style="color:#e6db74"&gt;&amp;#34;Message is `{Message}`&amp;#34;&lt;/span&gt;, item.Message);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pretty simple, right?&lt;/p&gt;
&lt;p&gt;We pass a &lt;code&gt;ChannelReader&amp;lt;Model&amp;gt;&lt;/code&gt; to the constructor&lt;/p&gt;
&lt;p&gt;In the first &lt;code&gt;while&lt;/code&gt;, we wait for a message to be available in the channel. When there&amp;rsquo;s one, we enter the second while to read every messages available until exiting.&lt;/p&gt;
&lt;p&gt;We could simplify this into a single while, but this version is a little more performant since it always to get multiple messages from a channel without having to &lt;code&gt;await&lt;/code&gt;. More information on &lt;a href="https://devblogs.microsoft.com/dotnet/an-introduction-to-system-threading-channels/"&gt;An Introduction to System.Threading.Channels&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="scoped-services"&gt;Scoped services&lt;/h3&gt;
&lt;p&gt;Note that the &lt;code&gt;BackgroundService&lt;/code&gt; here is a singleton, meaning that it won&amp;rsquo;t be able to access scoped services, such as EntityFramework Core&amp;rsquo;s &lt;code&gt;DbContext&lt;/code&gt;, which is often registered as scope.&lt;/p&gt;
&lt;p&gt;To get around that, you should inject an &lt;code&gt;IServiceProvider&lt;/code&gt; into your background service. Depending on performance consideration, I&amp;rsquo;d typically start with creating a new scope per incoming message, and looking into batching if there&amp;rsquo;s an issue.&lt;/p&gt;
&lt;h2 id="wiring-the-dependency-injection"&gt;Wiring the dependency injection&lt;/h2&gt;
&lt;p&gt;We start by wiring the channel itself, as long as its reader and writer.&lt;/p&gt;
&lt;p&gt;Note that I&amp;rsquo;m using minimal API syntax here, but it should be easy to adapt to the older style syntax like ASP.NET Core 5.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;builder.Services.AddSingleton(Channel.CreateUnbounded&amp;lt;Model&amp;gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;builder.Services.AddSingleton(p =&amp;gt; p.GetRequiredService&amp;lt;Channel&amp;lt;Model&amp;gt;&amp;gt;().Reader);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;builder.Services.AddSingleton(p =&amp;gt; p.GetRequiredService&amp;lt;Channel&amp;lt;Model&amp;gt;&amp;gt;().Writer);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;CreateUnbounded&lt;/code&gt; means that there won&amp;rsquo;t be any item limits on the channel, other than the amount of memory available to your application.&lt;/p&gt;
&lt;p&gt;then, we wire the service.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;builder.Services.AddHostedService&amp;lt;ModelService&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="setting-up-the-route"&gt;Setting up the route&lt;/h2&gt;
&lt;p&gt;We then setup a route to receive the messages. To make things easier, I made one that receives an array.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app.MapPost(&lt;span style="color:#e6db74"&gt;&amp;#34;/messages&amp;#34;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; (ChannelWriter&amp;lt;Model&amp;gt; writer, ICollection&amp;lt;Model&amp;gt; models, CancellationToken cancellationToken) =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;foreach&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; model &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; models)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; writer.WriteAsync(model, cancellationToken);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; StatusCodes.Status202Accepted;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="testing-it"&gt;Testing it&lt;/h2&gt;
&lt;p&gt;You can try it by making a POST to &lt;code&gt;/Messages&lt;/code&gt; with a payload such as&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; { &lt;span style="color:#f92672"&gt;&amp;#34;Message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;hello world&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; { &lt;span style="color:#f92672"&gt;&amp;#34;Message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;hello too&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; { &lt;span style="color:#f92672"&gt;&amp;#34;Message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;hello three&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you want to make sure that the request returns before the messages are processed, you can add a &lt;code&gt;Task.Delay&lt;/code&gt; in &lt;code&gt;ModelService&lt;/code&gt; after each log message. The messages will slowly scroll in your application&amp;rsquo;s console, long after the request has completed.&lt;/p&gt;
&lt;h1 id="parting-words"&gt;Parting words&lt;/h1&gt;
&lt;p&gt;This was a very simple demo of channels with ASP.NET Core. You can find a working demo at &lt;a href="https://github.com/EricStG/AspNetCoreChannels"&gt;https://github.com/EricStG/AspNetCoreChannels&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re considering using channels in your application, consider spending some time configuring your channels. For example, I&amp;rsquo;d look at whether I should use a bounded or unbounded channel, and if I&amp;rsquo;ll be using a single reader or writer. Having it properly configured will improve performance and avoid unplanned nastiness.&lt;/p&gt;</description></item><item><title>Editorconfig integration with Visual Studio and .NET</title><link>https://esg.dev/posts/editorconfig-with-visual-studio/</link><pubDate>Sun, 21 Feb 2021 19:01:55 -0500</pubDate><guid>https://esg.dev/posts/editorconfig-with-visual-studio/</guid><description>&lt;p&gt;If you are not familiar with &lt;a href="https://editorconfig.org/"&gt;EditorConfig&lt;/a&gt; files, you&amp;rsquo;re missing out&lt;/p&gt;
&lt;p&gt;Today, I&amp;rsquo;ll walk you through some of the basic features, as well as some extensions to the format supported by Visual Studio and the .NET tools.&lt;/p&gt;
&lt;h1 id="the-basics"&gt;The basics&lt;/h1&gt;
&lt;p&gt;Editorconfig is a way to define some coding styles by adding files called &lt;code&gt;.editorconfig&lt;/code&gt; to your code repository.
There&amp;rsquo;s pretty wide support from editors out there that supports it either natively, like &lt;a href="https://docs.microsoft.com/en-us/visualstudio/ide/create-portable-custom-editor-options?view=vs-2019"&gt;Visual Studio&lt;/a&gt;, or through add-ons, like &lt;a href="https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig"&gt;Visual Studio Code&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="hierarchy"&gt;Hierarchy&lt;/h2&gt;
&lt;p&gt;Editorconfig us a hierarchical configuration for code style, meaning that when a code file is open, it will apply every settings from &lt;code&gt;.editorconfig&lt;/code&gt; files it finds, from the file&amp;rsquo;s folder, up to the root of the file system.&lt;/p&gt;
&lt;p&gt;The closer to the code file the config is, the higher the priority.&lt;/p&gt;
&lt;p&gt;You can make prevent scanning parent directories by specifying &lt;code&gt;root = true&lt;/code&gt; in your &lt;code&gt;.editorconfig file&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="syntax"&gt;Syntax&lt;/h2&gt;
&lt;p&gt;Editorconfig files are split into sections. Each section starts with a file selector, as determined by square brackets &lt;code&gt;[]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This selector can apply to every file like &lt;code&gt;[*]&lt;/code&gt;, a simple wildcard like &lt;code&gt;[*.json]&lt;/code&gt;, or a list of extensions like &lt;code&gt;[*.{cs, vb}]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Consider the following example:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;root = true

[*]
indent_style = space

[*.{cs,vb}]
indent_size = 4
insert_final_newline = true
charset = utf-8-bom
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;First, we have &lt;code&gt;root = true&lt;/code&gt;, meaning that we won&amp;rsquo;t be scanning any parent folders for more &lt;code&gt;.editorconfig&lt;/code&gt; files.&lt;/p&gt;
&lt;p&gt;Then, we have a &lt;code&gt;[*]&lt;/code&gt; block that applies to every file, saying we will be using &lt;a href="https://www.youtube.com/watch?v=SsoOG6ZeyUI"&gt;space&lt;/a&gt; to indent.&lt;/p&gt;
&lt;p&gt;And last, we have the &lt;code&gt;[*.{cs,vb}]&lt;/code&gt; block that will apply to all files with a &lt;code&gt;.cs&lt;/code&gt; or &lt;code&gt;.vb&lt;/code&gt; extension.
Those files will have a indent of 4 (using spaces since the content of the &lt;code&gt;[*]&lt;/code&gt; block is still being applied), we want to ensure the presence of an empty line at the end of each file, and we are using the UTF-8 character set with the byte order mark.&lt;/p&gt;
&lt;p&gt;Tip: If using git, you will want to make sure that your &lt;a href="https://git-scm.com/docs/gitattributes"&gt;gitattribute&lt;/a&gt; don&amp;rsquo;t conflict with your &lt;code&gt;.editorconfig&lt;/code&gt; settings, otherwise your editor might trying to undo what git is doing on checkout, causing a bunch of changes that aren&amp;rsquo;t wanted.&lt;/p&gt;
&lt;h2 id="net-specific-extensions"&gt;.NET specific extensions&lt;/h2&gt;
&lt;p&gt;With the Roslyn compiler, there&amp;rsquo;s a number of extensions to the Editorconfig format that allows to configure code analysis.&lt;/p&gt;
&lt;p&gt;For example, you can use Editorconfig to tell whether using clauses should be sorted with the &lt;code&gt;System&lt;/code&gt; namespace first&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[*.{cs,vb}]
dotnet_sort_system_directives_first = true
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You want contributors to use &lt;code&gt;var&lt;/code&gt; for C# built-in types? Done!&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;csharp_style_var_for_built_in_types = true
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can even make it an error:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;csharp_style_var_for_built_in_types = true:error
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can also configure casing, as well as prefix and suffix. This will give a warning for any fields that are not camel case, and not prefixed with an underscore &lt;code&gt;_&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;dotnet_naming_rule.instance_fields_should_be_camel_case.severity = warning
dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields
dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style
dotnet_naming_symbols.instance_fields.applicable_kinds = field
dotnet_naming_style.instance_field_style.capitalization = camel_case
dotnet_naming_style.instance_field_style.required_prefix = _
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;See the &lt;a href="https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview"&gt;official documentation&lt;/a&gt; for more options. The [Roslyn .editorconfig] also has number of good examples.&lt;/p&gt;
&lt;h2 id="configuring-code-analysis"&gt;Configuring code analysis&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re familiar with Visual Studio and Code Analysis, you might have had to configure it through xml via &lt;a href="https://docs.microsoft.com/en-us/visualstudio/code-quality/using-rule-sets-to-group-code-analysis-rules?view=vs-2019"&gt;.ruleset&lt;/a&gt; files.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s no longer necessary, as it can be done through Editorconfig!&lt;/p&gt;
&lt;p&gt;All you need is a line that reads &lt;code&gt;dotnet_diagnostic.&amp;lt;Code&amp;gt;.severity = &amp;lt;Severity&amp;gt;&lt;/code&gt;, such as:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[*.{cs,vb}]
dotnet_diagnostic.RCS1007.severity = error
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Whenever you a small or a large team, having common code styles in your project can help minimize head scratching when reading a teammate&amp;rsquo;s code (or let&amp;rsquo;s be honest, your past self&amp;rsquo;s code as well).
With Editorconfig, you have a standard way that&amp;rsquo;s not tied to specific technologies to help make those styling standard happens.&lt;/p&gt;
&lt;p&gt;What coding styles do you apply to your project?&lt;/p&gt;</description></item><item><title>Using HttpClient with System.Text.Json</title><link>https://esg.dev/posts/httpclient-with-system-text-json/</link><pubDate>Fri, 22 Jan 2021 17:01:55 -0500</pubDate><guid>https://esg.dev/posts/httpclient-with-system-text-json/</guid><description>&lt;p&gt;When working with JSON and &lt;code&gt;HttpClient&lt;/code&gt; in the .NET Framework, it was pretty common to add a reference to &lt;a href="https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Client/"&gt;Microsoft.AspNet.WebApi.Client&lt;/a&gt;, which added a number of extension methods to &lt;code&gt;HttpClient&lt;/code&gt; and &lt;code&gt;HttpResponseMessage&lt;/code&gt; that made it simpler to send and receive JSON documents.&lt;/p&gt;
&lt;p&gt;That package still work with .NET Core, but it has a dependency on the &lt;a href="https://www.nuget.org/packages/Newtonsoft.Json/"&gt;Newtonsoft.Json&lt;/a&gt;. Since .NET Core 3+ comes with &lt;a href="https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to?pivots=dotnet-5-0"&gt;System.Text.Json&lt;/a&gt;, a more async friendly JSON parser, it might be worth making the switch, espcially in newer projects.&lt;/p&gt;
&lt;p&gt;Conveniently, there&amp;rsquo;s also a package called &lt;a href="https://www.nuget.org/packages/System.Net.Http.Json/"&gt;System.Net.Http.Json&lt;/a&gt; that includes extension methods to make your serialization and deserializion easier.&lt;/p&gt;
&lt;h1 id="microsoftaspnetwebapiclient"&gt;Microsoft.AspNet.WebApi.Client&lt;/h1&gt;
&lt;p&gt;This section will show a few calls made with &lt;code&gt;Microsoft.AspNet.WebApi.Client&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The assumption is that you have an &lt;code&gt;HttpClient&lt;/code&gt; already instanciated with the name &lt;code&gt;client&lt;/code&gt;, a request model called &lt;code&gt;RequestModel&lt;/code&gt;, and a response model called &lt;code&gt;ResponseModel&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="a-basic-get"&gt;A basic GET&lt;/h2&gt;
&lt;p&gt;First, we get a &lt;code&gt;HttpResponseMessage&lt;/code&gt; from the client by making a request.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HttpResponseMessage response = &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; client.GetAsync(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, we using the generic verion of the &lt;code&gt;ReadAsAsync&amp;lt;T&amp;gt;&lt;/code&gt; extension method to read and deserialize the JSON document into our object.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Task&amp;lt;ResponseModel&amp;gt; responseModel = &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; response.Content.ReadAsAsync&amp;lt;ResponseModel&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="a-post-with-a-request-document"&gt;A POST with a request document&lt;/h2&gt;
&lt;p&gt;Sending a document is also pretty straightforward.&lt;/p&gt;
&lt;p&gt;First, we initialize our request model.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RequestModel requestModel = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; RequestModel();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, we make our request, including our model that will get serialized through the &lt;code&gt;PostAsJsonAsync&lt;/code&gt; extension method.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HttpResponseMessage response = &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; client.PostAsJsonAsync(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;, requestModel);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, we read the response model, just like we did for the GET.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ResponseModel responseModel = &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; response.Content.ReadAsAsync&amp;lt;ResponseModel&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="systemnethttpjson"&gt;System.Net.Http.Json&lt;/h1&gt;
&lt;p&gt;Here, we&amp;rsquo;ll be doing the same exact thing, but using the extension methods in &lt;code&gt;System.Net.Http.Json&lt;/code&gt; instead.&lt;/p&gt;
&lt;h2 id="a-basic-get-1"&gt;A basic GET&lt;/h2&gt;
&lt;p&gt;Again, we get a &lt;code&gt;HttpResponseMessage&lt;/code&gt; from the client by making a request. This is the same as for &lt;code&gt;Microsoft.AspNet.WebApi.Client&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HttpResponseMessage response = &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; client.GetAsync(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, we using the generic verion of the &lt;code&gt;ReadFromJsonAsync&amp;lt;T&amp;gt;&lt;/code&gt; extension method to read and serialize the JSON document into our object.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ResponseModel responseModel = &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; response.Content.ReadFromJsonAsync&amp;lt;ResponseModel&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that if you don&amp;rsquo;t need to do any processing on the &lt;code&gt;HttpResponseMessage&lt;/code&gt;, there&amp;rsquo;s also a convenience method called &lt;code&gt;GetFromJsonAsync&lt;/code&gt; so you can skip that step entirely.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ResponseModel responseModel = &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; client.GetFromJsonAsync&amp;lt;ResponseModel&amp;gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="a-post-with-a-request-document-1"&gt;A POST with a request document&lt;/h2&gt;
&lt;p&gt;Sending a document is also pretty straightforward.&lt;/p&gt;
&lt;p&gt;First, we initialize our request model.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RequestModel requestModel = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; RequestModel();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, we make our request, including our model that will get serialized through the &lt;code&gt;PostAsJsonAsync&lt;/code&gt; extension method, which conveniently has the same name as the extension method from &lt;code&gt;Microsoft.AspNet.WebApi.Client&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HttpResponseMessage response = &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; client.PostAsJsonAsync(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;, requestModel);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, we read the response model, just like we did for the GET.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ResponseModel responseModel = &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; response.Content.ReadFromJsonAsync&amp;lt;ResponseModel&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="final-notes"&gt;Final notes&lt;/h1&gt;
&lt;p&gt;While serializing and deserializing documents with the &lt;code&gt;HttpClient&lt;/code&gt; isn&amp;rsquo;t particularly challenging, it does lead to a fair amount of repetition, so using these extension methods (or rolling your own!) should make your code look a lot leaner.&lt;/p&gt;</description></item><item><title>Serilog Do's and Don'ts</title><link>https://esg.dev/posts/serilog-dos-and-donts/</link><pubDate>Sun, 12 Jul 2020 18:08:20 -0400</pubDate><guid>https://esg.dev/posts/serilog-dos-and-donts/</guid><description>&lt;p&gt;&lt;a href="https://serilog.net/"&gt;Serilog&lt;/a&gt; is a structured logger for &lt;a href="https://dotnet.microsoft.com/"&gt;.NET&lt;/a&gt;, and as such, for folks who are used to traditional console loggers, requires the developer to think about their logs a little more than normal.&lt;/p&gt;
&lt;p&gt;This post is not meant to be a how to, but rather a non-comprehensive list of mistakes I&amp;rsquo;ve seen over time.&lt;/p&gt;
&lt;h2 id="templating"&gt;Templating&lt;/h2&gt;
&lt;p&gt;One thing to keep in mind is that when writing an event to the log, you are not passing a message, but a &lt;em&gt;template&lt;/em&gt;. The tags you pass within curly branches do have meaning, and will be used to add data the event. It is therefore important to not format the message before passing logging it.&lt;/p&gt;
&lt;p&gt;Consider a logger with a basic console sink&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; textLogger = Log.Logger = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; LoggerConfiguration()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .WriteTo.Console()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .CreateLogger();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With two very basic statements&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;textLogger.Information(&lt;span style="color:#e6db74"&gt;$&amp;#34;Hello {world}&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;textLogger.Information(&lt;span style="color:#e6db74"&gt;&amp;#34;Hello {value}&amp;#34;&lt;/span&gt;, world);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Will output:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[18:19:18 INF] Hello World
[18:19:18 INF] Hello World
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Can you spot the difference? Spoiler: There isn&amp;rsquo;t one! So what&amp;rsquo;s the problem then?&lt;/p&gt;
&lt;p&gt;Well, let&amp;rsquo;s use the &lt;a href="https://github.com/serilog/serilog-formatting-compact"&gt;JSON Formatter&lt;/a&gt; instead:&lt;/p&gt;
&lt;p&gt;(note: I&amp;rsquo;m using the &lt;a href="https://github.com/serilog/serilog-formatting-compact#rendered-events"&gt;Rendered&lt;/a&gt; formatter so we can see the rendered template within each event)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; jsonLogger = Log.Logger = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; LoggerConfiguration()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .WriteTo.Console(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; RenderedCompactJsonFormatter())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .CreateLogger();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With the same statements&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;jsonLogger.Information(&lt;span style="color:#e6db74"&gt;$&amp;#34;Hello {world}&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;jsonLogger.Information(&lt;span style="color:#e6db74"&gt;&amp;#34;Hello {value}&amp;#34;&lt;/span&gt;, world);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Will give us:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@t&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2020-07-12T22:26:19.6616913Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@m&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Hello World&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@i&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;00ba5a14&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@t&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2020-07-12T22:26:19.6659300Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@m&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Hello \&amp;#34;World\&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@i&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;40c34e37&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;World&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When using &lt;code&gt;textLogger.Information(&amp;quot;Hello {value}&amp;quot;, world)&lt;/code&gt;, there is now a field called &lt;code&gt;value&lt;/code&gt; with the argument we passed to the logging statements.
In addition, the &lt;code&gt;@i&lt;/code&gt; field will have the same value every time this statement will run, so you have a fixed item to search for if you want to search for every value being logged by this particular statement.&lt;/p&gt;
&lt;h3 id="tldr"&gt;TLDR&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;DON&amp;rsquo;T&lt;/strong&gt; format messages before passing them to the logger&lt;/p&gt;
&lt;h2 id="destructuring"&gt;Destructuring&lt;/h2&gt;
&lt;p&gt;One of the benefit of having a structured logger versus a flat one, is, well, &lt;em&gt;structure&lt;/em&gt;. You are not limited to logging simple values, you can include entire object graphs. This process is called &lt;em&gt;Destructuring&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s consider the following classes&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestInnerClass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; SomeOtherProperty { &lt;span style="color:#66d9ef"&gt;get&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestClass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; SomeProperty { &lt;span style="color:#66d9ef"&gt;get&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; TestInnerClass SomeInnerClass { &lt;span style="color:#66d9ef"&gt;get&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;set&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s initialize an instance&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; obj = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; TestClass
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; SomeInnerClass = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; TestInnerClass
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; SomeOtherProperty = &lt;span style="color:#e6db74"&gt;&amp;#34;I am a property within a child&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; SomeProperty = &lt;span style="color:#e6db74"&gt;&amp;#34;I am a property at the root level&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And let&amp;rsquo;s include it a logging statement&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;jsonLogger.Information(&lt;span style="color:#e6db74"&gt;&amp;#34;Useful log for object {object}&amp;#34;&lt;/span&gt;, obj);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will output&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@t&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2020-07-12T22:43:28.1191964Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@m&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Useful log for object \&amp;#34;SerilogTest.TestClass\&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@i&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;146d94ee&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;SerilogTest.TestClass&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What? &lt;code&gt;SerilogTest.TestClass&lt;/code&gt;? By default, Serilog will simply call &lt;code&gt;ToString&lt;/code&gt; on your object. To destructure it, simply add &lt;code&gt;a&lt;/code&gt; before the field in your template&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;jsonLogger.Information(&lt;span style="color:#e6db74"&gt;&amp;#34;Useful log for object {@object}&amp;#34;&lt;/span&gt;, obj);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the output will be&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@t&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2020-07-12T22:43:28.1525906Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@m&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Useful log for object TestClass { SomeProperty: \&amp;#34;I am a property at the root level\&amp;#34;, SomeInnerClass: TestInnerClass { SomeOtherProperty: \&amp;#34;I am a property within a child\&amp;#34; } }&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@i&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;99aea052&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#f92672"&gt;&amp;#34;SomeProperty&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;I am a property at the root level&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#f92672"&gt;&amp;#34;SomeInnerClass&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#f92672"&gt;&amp;#34;SomeOtherProperty&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;I am a property within a child&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#f92672"&gt;&amp;#34;$type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;TestInnerClass&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#f92672"&gt;&amp;#34;$type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;TestClass&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, the entire object graph was included within the event. So much contextual information!&lt;/p&gt;
&lt;p&gt;But what if there are properties that you do not want to log? After all, it&amp;rsquo;s typically frowned upon to include information like passwords or personally identifiable information within them.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s reconfigure the logger to only log &lt;code&gt;SomeProperty&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; jsonLogger = Log.Logger = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; LoggerConfiguration()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .WriteTo.Console(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; RenderedCompactJsonFormatter())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .Destructure.ByTransforming&amp;lt;TestClass&amp;gt;(c =&amp;gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; { c.SomeProperty })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .CreateLogger();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Will give us&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@t&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2020-07-12T22:49:44.7386801Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@m&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Useful log for object { SomeProperty: \&amp;#34;I am a property at the root level\&amp;#34; }&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@i&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;99aea052&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#f92672"&gt;&amp;#34;SomeProperty&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;I am a property at the root level&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We only included the property we wanted!&lt;/p&gt;
&lt;p&gt;Note that there are different ways to configure destructuring. A good start in the (awesomely named) &lt;a href="https://github.com/destructurama"&gt;Destructurama!&lt;/a&gt; organization.&lt;/p&gt;
&lt;h3 id="type-conflicts"&gt;Type conflicts&lt;/h3&gt;
&lt;p&gt;Just a side note about destructing objects regarding conflicting types. Some log aggregating services will use the parameter names within templates to define types.&lt;/p&gt;
&lt;p&gt;An example of this is Elasticsearch.&lt;/p&gt;
&lt;p&gt;With the following statement&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;jsonLogger.Information(&lt;span style="color:#e6db74"&gt;&amp;#34;Hello {value}&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;world&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;jsonLogger.Information(&lt;span style="color:#e6db74"&gt;&amp;#34;Hello {value}&amp;#34;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; { &lt;span style="color:#66d9ef"&gt;value&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;world&amp;#34;&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since the first statement will generate a string field called &amp;ldquo;value&amp;rdquo; in the mapping, the second statement will not be logged, as Elasticsearch will complain about trying to fit an object within a string field.&lt;/p&gt;
&lt;h3 id="tldr-1"&gt;TLDR&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;DO&lt;/strong&gt; include object graph when they add contextual information to your statements
&lt;strong&gt;AVOID&lt;/strong&gt; including unnecessary objects. They can clutter your logs, and if you&amp;rsquo;re pushing them to external services that charges by the size of logs you send them, it will quite literally cost you.
&lt;strong&gt;DO&lt;/strong&gt; Be mindful of conflicts that can happen with your parameter names&lt;/p&gt;
&lt;h2 id="exceptions"&gt;Exceptions&lt;/h2&gt;
&lt;p&gt;The one thing to remember is that Serilog&amp;rsquo;s methods all have an overload that accepts an &lt;code&gt;Exception&lt;/code&gt; as the first parameter.&lt;/p&gt;
&lt;p&gt;So instead of writing statements including the exception as part of the message template&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; exception = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Exception(&lt;span style="color:#e6db74"&gt;&amp;#34;Top exception&amp;#34;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Exception(&lt;span style="color:#e6db74"&gt;&amp;#34;Inner exception&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;jsonLogger.Information(&lt;span style="color:#e6db74"&gt;&amp;#34;Exception! {exception}&amp;#34;&lt;/span&gt;, exception);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Which will include the exception within the &lt;code&gt;exception&lt;/code&gt; parameter&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@t&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2020-07-12T23:01:14.1165769Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@m&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Exception! \&amp;#34;System.Exception: Top exception\r\n ---&amp;gt; System.Exception: Inner exception\r\n --- End of inner exception stack trace ---\&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@i&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;e22b2a9a&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;exception&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;System.Exception: Top exception\r\n ---&amp;gt; System.Exception: Inner exception\r\n --- End of inner exception stack trace ---&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Simply include the exception as the first argument&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; exception = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Exception(&lt;span style="color:#e6db74"&gt;&amp;#34;Top exception&amp;#34;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Exception(&lt;span style="color:#e6db74"&gt;&amp;#34;Inner exception&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;jsonLogger.Information(exception, &lt;span style="color:#e6db74"&gt;&amp;#34;Exception&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And now all logged exceptions will be within the &lt;code&gt;@x&lt;/code&gt; field.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@t&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2020-07-12T23:01:14.1532597Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@m&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Exception&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@i&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;7d53da39&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#f92672"&gt;&amp;#34;@x&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;System.Exception: Top exception\r\n ---&amp;gt; System.Exception: Inner exception\r\n --- End of inner exception stack trace ---&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that this behaviour can change depending on your sink. For example, the &lt;a href="https://github.com/serilog/serilog-sinks-elasticsearch"&gt;Elasticsearch sink&lt;/a&gt; will include the exceptions within an object array.&lt;/p&gt;
&lt;h3 id="tldr-2"&gt;TLDR&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;DO&lt;/strong&gt; use Serilog&amp;rsquo;s overloads to pass exceptions instead of using the argument list.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Those were just a few basic things to keep in mind when using Serilog. I stringly suggest visiting the &lt;a href="https://github.com/serilog/serilog/wiki/Writing-Log-Events"&gt;Writing log events&lt;/a&gt; page for more information.&lt;/p&gt;</description></item><item><title>Introduction to Action Constraints in ASP.NET Core</title><link>https://esg.dev/posts/introduction-to-action-constraints/</link><pubDate>Mon, 02 Dec 2019 18:45:08 -0500</pubDate><guid>https://esg.dev/posts/introduction-to-action-constraints/</guid><description>&lt;p&gt;Ever had to integrate a 3rd party webhook into your application, only to realize that they won&amp;rsquo;t let you configure a different address for each type of events?&lt;/p&gt;
&lt;p&gt;A few years ago, I had to do just that for Github webhooks. Thankfully, they provide a HTTP header that describe the type of event being sent.&lt;/p&gt;
&lt;p&gt;At the time, I used a middleware to modify the route, but there are cleaner solutions out there. In this case, I could&amp;rsquo;ve used a feature in ASP.NET Core called &amp;ldquo;Action Constraints&amp;rdquo;, which allow you to impose constraint on otherwise identical routes.&lt;/p&gt;
&lt;p&gt;Note: This is a follow-up on a question I had a few years ago on &lt;a href="https://stackoverflow.com/questions/39302121/header-based-routing-in-asp-net-core"&gt;StackOverflow&lt;/a&gt;, and I decided to dig a little deeper.&lt;/p&gt;
&lt;h2 id="action-contraint-attribute"&gt;Action Contraint Attribute&lt;/h2&gt;
&lt;p&gt;ASP.NET Core has an interface called &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.actionconstraints.iactionconstraint?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev16.query%3FappId%3DDev16IDEF1%26l%3DEN-US%26k%3Dk(Microsoft.AspNetCore.Mvc.ActionConstraints.IActionConstraint);k(DevLang-csharp)%26rd%3Dtrue&amp;amp;view=aspnetcore-3.0"&gt;&lt;code&gt;IActionConstraint&lt;/code&gt;&lt;/a&gt; that can be implemented by an &lt;code&gt;Attribute&lt;/code&gt; class, which has only 2 members:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Order&lt;/code&gt;, which will determine what order your constraint will be evaluated on (lowest number first)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Accept(ActionConstraintContext)&lt;/code&gt;, which needs to return a boolean that will tell the framework whether to use this route&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s see what an implementation can look like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;HeaderConstraintAttribute&lt;/span&gt; : Attribute, IActionConstraint
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; Order =&amp;gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; Header { &lt;span style="color:#66d9ef"&gt;get&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; Value { &lt;span style="color:#66d9ef"&gt;get&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; HeaderConstraintAttribute(&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; header, &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;value&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Header = header;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Value = &lt;span style="color:#66d9ef"&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; Accept(ActionConstraintContext context)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (context.RouteContext.HttpContext.Request.Headers.TryGetValue(Header, &lt;span style="color:#66d9ef"&gt;out&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;value&lt;/span&gt;) &amp;amp;&amp;amp; &lt;span style="color:#66d9ef"&gt;value&lt;/span&gt;.Any())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;value&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;] == Value;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Simple enough, right? Look for a matching &lt;code&gt;Header&lt;/code&gt; in the &lt;code&gt;HttpContext&lt;/code&gt;, and if present, match it with &lt;code&gt;Value&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, let&amp;rsquo;s update our controller.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;[Route(&amp;#34;api/[controller]&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;)]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;[ApiController]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ActionController&lt;/span&gt; : ControllerBase
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt; [HttpGet]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt; [HeaderConstraint(&amp;#34;MyHeader&amp;#34;, &amp;#34;A&amp;#34;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; GetStringA()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt; [HttpGet]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt; [HeaderConstraint(&amp;#34;MyHeader&amp;#34;, &amp;#34;B&amp;#34;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; GetStringB()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;B&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are 2 things to notice here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Both functions are HTTP Get, to the same route (&lt;code&gt;/api/action&lt;/code&gt; in this case)&lt;/li&gt;
&lt;li&gt;We&amp;rsquo;ve added the &lt;code&gt;HeaderConstraintAttribute&lt;/code&gt; on each route&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, try using your HTTP API tool of choice, such as &lt;a href="https://www.getpostman.com/"&gt;Postman&lt;/a&gt; or &lt;a href="https://insomnia.rest/"&gt;Insomnia&lt;/a&gt;, and do a GET request to &lt;code&gt;/api/action&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You should be getting a 404 Not Found error, which makes sense, since both routes are contrained, and neither are matching the contraint.&lt;/p&gt;
&lt;p&gt;Now, try passing a header called &lt;code&gt;MyHeader&lt;/code&gt; with the value &lt;code&gt;A&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Surprise! The API returns &lt;code&gt;A&lt;/code&gt;. Change the header value to &lt;code&gt;B&lt;/code&gt;, the API now returns &lt;code&gt;B&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is obviously a very simple scenario, but when dealing with 3rd party APIs that sends different requests to the same address, it&amp;rsquo;s a great way to keep the routes seperated, use strong-typed classes and avoid using things like &lt;code&gt;dynamic&lt;/code&gt; variables.&lt;/p&gt;
&lt;p&gt;You can view the entire code written for this project on &lt;a href="https://github.com/EricStG/ActionConstraintDemo"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;</description></item></channel></rss>