<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Rahul Nath]]></title><description><![CDATA[I code, write, and teach .NET | Tech YouTuber & Content Creator with a Full Time Job]]></description><link>https://rahulpnath.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sun, 30 Aug 2026 22:09:12 GMT</lastBuildDate><atom:link href="https://rahulpnath.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[5 Recommended Patterns When  Using Cancellation Token in .NET]]></title><description><![CDATA[Are you passing around the CancellationToken to all your functions blindly?
Stop using Cancellation Tokens the wrong way in .NET!
CancellationTokens in .NET allows the caller to express lost interest in the result of an operation. This can be at a Fu...]]></description><link>https://rahulpnath.hashnode.dev/5-recommended-patterns-when-using-cancellation-token-in-net</link><guid isPermaLink="true">https://rahulpnath.hashnode.dev/5-recommended-patterns-when-using-cancellation-token-in-net</guid><category><![CDATA[dotnet]]></category><category><![CDATA[dotnetcore]]></category><category><![CDATA[C#]]></category><dc:creator><![CDATA[Rahul Nath]]></dc:creator><pubDate>Wed, 03 Apr 2024 04:06:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1712116943799/5fd0b40f-40e2-4480-9d1d-925cbb023b4c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Are you passing around the CancellationToken to all your functions blindly?</p>
<p>Stop using Cancellation Tokens the <strong>wrong</strong> way in .NET!</p>
<p>CancellationTokens in .NET allows the caller to express lost interest in the result of an operation. This can be at a Function level, class level, API endpoint, etc.</p>
<p>Does that mean all requests are the same and can be canceled similarly?</p>
<p>Short answer - No!</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/sWAk4YMK2go?si=C1TZeQz-YIBGjUpL">https://youtu.be/sWAk4YMK2go?si=C1TZeQz-YIBGjUpL</a></div>
<p> </p>
<p>In this post, let’s learn five good practices when using Cancellation Tokens in your application code.</p>
<blockquote>
<p><em>Cancellation tokens are a great way to implement cooperative cancellation of asynchronous or long-running synchronous operations.</em></p>
</blockquote>
<p>Cancellation tokens allow us to cancel out long-running processes and HTTP requests to other applications, thereby reducing the time our server spends on requests that no one cares about anymore.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.rahulpnath.com/blog/cancellation-token-dotnet/">https://www.rahulpnath.com/blog/cancellation-token-dotnet/</a></div>
<p> </p>
<h2 id="heading-the-problem-with-cancelling-all-functions">The Problem With Cancelling All Functions</h2>
<p>Any time you chain multiple cancellable operations together by calling them together (either parallelly or sequentially), it's possible to leave the application in an invalid state.</p>
<p>Let's look at the same example as in the <a target="_blank" href="https://www.rahulpnath.com/blog/cancellation-token-dotnet/">previous post</a>, where we are uploading a file to Amazon S3.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.rahulpnath.com/blog/amazon-s3-dotnet/">https://www.rahulpnath.com/blog/amazon-s3-dotnet/</a></div>
<p> </p>
<p>This problem is not limited to Amazon S3 but applies to any cancellable operations chained together.</p>
<p>So, continue reading even if you don't understand how Amazon S3 works or how to use it from a .NET application. But if you want to learn more about Amazon S3, check out the post below.</p>
<pre><code class="lang-csharp">app.MapPost(<span class="hljs-string">"/upload-large-file"</span>, <span class="hljs-keyword">async</span> ([FromForm] FileUploadRequest request, CancellationToken cancellationToken) =&gt;
    {
        <span class="hljs-keyword">try</span>
        {
            <span class="hljs-keyword">var</span> s3Client = <span class="hljs-keyword">new</span> AmazonS3Client();
            <span class="hljs-keyword">await</span> s3Client.PutObjectAsync(<span class="hljs-keyword">new</span> PutObjectRequest()
            {
                BucketName = <span class="hljs-string">"user-service-large-messages"</span>,
                Key = <span class="hljs-string">$"<span class="hljs-subst">{Guid.NewGuid()}</span> - <span class="hljs-subst">{request.File.FileName}</span>"</span>,
                InputStream = request.File.OpenReadStream()
            }, cancellationToken);

            <span class="hljs-keyword">await</span> PerformAdditionalTasks(CancellationToken.None);
            <span class="hljs-keyword">return</span> Results.NoContent();
        }
        <span class="hljs-keyword">catch</span> (OperationCanceledException e)
        {
            <span class="hljs-keyword">return</span> Results.StatusCode(<span class="hljs-number">499</span>);
        }
    })
    .WithName(<span class="hljs-string">"UploadLargeFile"</span>)
    .DisableAntiforgery()
    .WithOpenApi();

<span class="hljs-function"><span class="hljs-keyword">async</span> Task <span class="hljs-title">PerformAdditionalTasks</span>(<span class="hljs-params">CancellationToken cancellationToken</span>)</span>
{
    <span class="hljs-keyword">await</span> Task.Delay(<span class="hljs-number">1000</span>, cancellationToken);

    <span class="hljs-keyword">var</span> snsClient = <span class="hljs-keyword">new</span> AmazonSimpleNotificationServiceClient();
    <span class="hljs-keyword">await</span> snsClient.PublishAsync(<span class="hljs-keyword">new</span> PublishRequest()
    {
        TopicArn = <span class="hljs-string">"&lt;SNS TOPIC ARN&gt;"</span>,
        Message = <span class="hljs-string">"UserUploadedFileEvent"</span>
    }, cancellationToken);
</code></pre>
<p>The API endpoint takes in the <code>CancellationToken</code> and passes that on to the Amazon S3 client <code>PutObjectAsync</code> method.</p>
<p>If the user cancels out of the upload process midway through uploading a large file, the file upload is also canceled and discarded from S3.</p>
<p>Let's say in our business functionality, we have to do some additional work right after uploading a file to S3, represented by the <code>PerformASdditionalTasks</code> function below.</p>
<p>The <code>PerformAdditionalTasks</code> for now, it simulates work with a <code>Task.Delay</code> and publishes a message into the Amazon SNS notifying anyone else interested in the <code>UserUploadedFileEvent</code>.</p>
<p>The <code>PerformAdditionalTasks</code> also takes in the same <code>CancellationToken</code> passed into the API endpoint.</p>
<p>When invoking the API endpoint, if the user cancels out before the file upload is complete or waits until the end, everything works as expected.</p>
<p>However, the operation will be canceled if the user cancels the API request right after the file is uploaded to the S3 bucket while the server processes the additional tasks.</p>
<p><strong><em>This leaves the application in an invalid state.</em></strong></p>
<p>If the file is uploaded but hasn't completed the additional tasks or raised the event on the file upload, it will be left dangling in that S3 bucket.</p>
<p>This brings us to our first recommended practice when using CancellationToken in your .NET applications.</p>
<h2 id="heading-1-avoid-canceling-operations-after-side-effects">1. Avoid canceling operations after side-effects</h2>
<p>Once the application code has started making side effects don't cancel out of the operation.</p>
<p>In the above scenario, the application incurred a side effect after successfully uploading the file to the S3 bucket.</p>
<p>From this point on, canceling out the operation must be an intentional choice.</p>
<p>If it's decided to cancel the operation after that, necessary compensating action(s) must be taken. In our scenario, it will be to delete the uploaded file.</p>
<p>The application will continue processing and raise the events if not allowed to cancel. The function must return a success status code in this case and not throw an <code>OperationCancelledException</code>.</p>
<p>In this case, the function caller, which is any consumer of the API endpoint, must be ready to handle any cleanup activities in case the cancellation request is ignored and the processing is completed successfully.</p>
<h2 id="heading-2-optional-cancellation-token-on-public-api-and-required-elsewhere">2. Optional Cancellation Token on Public API and required elsewhere</h2>
<p>The function composing the different functions to perform a bigger task should be able to decide whether an individual component should be canceled.</p>
<p>To enable this, it's recommended to make the <code>CancellationToken</code> mandatory on the internal/private functions.</p>
<p>At the same time, the callee of the public function should have the flexibility of passing in a CancellationToken or ignoring it if it does not intend to cancel.</p>
<p>So make CancellationToken optional on your Public functions and mandatory on the internal/private functions (that can be cancelled).</p>
<p>In ASP.NET Core, since the framework automatically injects the CancellationToken, which is available as part of the RequestContext, making it optional or mandatory does not make much difference.</p>
<p>But you can see this pattern of keeping it optional in almost all the SDKs/Nuget packages.</p>
<p>For example, look at the two different methods from the Amazon S3 client and the SNS client below</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// S3 Client</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">virtual</span> Task&lt;PutObjectResponse&gt; <span class="hljs-title">PutObjectAsync</span>(<span class="hljs-params">
  PutObjectRequest request, 
  System.Threading.CancellationToken cancellationToken = <span class="hljs-keyword">default</span>(CancellationToken</span>))

  <span class="hljs-comment">// SNS Client</span>
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">virtual</span> Task&lt;PublishResponse&gt; <span class="hljs-title">PublishAsync</span>(<span class="hljs-params">
    PublishRequest request, 
    System.Threading.CancellationToken cancellationToken = <span class="hljs-keyword">default</span>(CancellationToken</span>))</span>
</code></pre>
<p>In both cases, the <code>CancellationToken</code> is optional and defaults <code>CancellationToken.None</code>.</p>
<h2 id="heading-3-use-cancellationtokennone-after-the-point-of-no-cancellation">3. Use CancellationToken.None after the point of 'no cancellation'</h2>
<p><code>CancellationToken.None</code> cannot be canceled; that is, its <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken.canbecanceled?view=net-8.0">CanBeCanceled</a> property is <code>false</code>.</p>
<p>Once the application has passed the point of 'no cancellation,' or in other words, has incurred side effects, you can pass on the <code>CancellationToken.None</code> after that point.</p>
<p>This ensures that even if the callee of the original public function cancels the token, the operation will run to completion/or a stable state.</p>
<p>Applying this to our original function means we do not pass on the cancellation token received from the ASP NET framework but pass on a <code>CancellationToken.None</code> after the file is uploaded to S3.</p>
<pre><code class="lang-csharp">app.MapPost(<span class="hljs-string">"/upload-large-file"</span>, <span class="hljs-keyword">async</span> ([FromForm] FileUploadRequest request, CancellationToken cancellationToken) =&gt;
    {
        <span class="hljs-keyword">try</span>
        {
            <span class="hljs-keyword">var</span> s3Client = <span class="hljs-keyword">new</span> AmazonS3Client();
            <span class="hljs-keyword">await</span> s3Client.PutObjectAsync(<span class="hljs-keyword">new</span> PutObjectRequest()
            {
                BucketName = <span class="hljs-string">"user-service-large-messages"</span>,
                Key = <span class="hljs-string">$"<span class="hljs-subst">{Guid.NewGuid()}</span> - <span class="hljs-subst">{request.File.FileName}</span>"</span>,
                InputStream = request.File.OpenReadStream()
            }, cancellationToken);

            <span class="hljs-keyword">await</span> PerformAdditionalTasks(CancellationToken.None);
            <span class="hljs-keyword">return</span> Results.NoContent();
        }
        <span class="hljs-keyword">catch</span> (OperationCanceledException e)
        {
            <span class="hljs-keyword">return</span> Results.StatusCode(<span class="hljs-number">499</span>);
        }
    })
</code></pre>
<p>In the updated code for our <code>POST</code> endpoint, once the file is uploaded to S3, it passes on the <code>CancellationToken.None</code> to the <code>PerformAdditionalTasks</code> function (and any other cancellable function after that point).</p>
<p>It's now the consumer of the API's responsibility to do necessary compensating work, if it requested cancellation but receives a successful response.</p>
<h2 id="heading-4-check-cancellationtokencanbecanceled">4. Check CancellationToken.CanBeCanceled</h2>
<p>Suppose your functions can be made more efficient when they can't be canceled. In that case, checking if the CancellationToken can be canceled and having a different implementation for the function code is recommended.</p>
<p>The best example of this is the <code>Task.Delay</code> method.</p>
<pre><code class="lang-csharp"> <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Task <span class="hljs-title">Delay</span>(<span class="hljs-params"><span class="hljs-keyword">uint</span> millisecondsDelay, TimeProvider timeProvider, CancellationToken cancellationToken</span>)</span> =&gt;
            cancellationToken.IsCancellationRequested ? FromCanceled(cancellationToken) :
            millisecondsDelay == <span class="hljs-number">0</span> ? CompletedTask :
            cancellationToken.CanBeCanceled ? <span class="hljs-keyword">new</span> DelayPromiseWithCancellation(millisecondsDelay, timeProvider, cancellationToken) :
            <span class="hljs-keyword">new</span> DelayPromise(millisecondsDelay, timeProvider);
</code></pre>
<p>Based on the <code>CancellationToken.CanBeCancelled</code> property, it switches between two different implementations using the <code>DelayPromiseWithCancellation</code> or <code>DelayPromise</code>.</p>
<p>It enables the function to be more efficient in scenarios where the token cannot be canceled, and it has to run till completion of the time passed to it.</p>
<h2 id="heading-5-ignore-cancellationtoken-if-work-is-quick">5. Ignore CancellationToken if work is quick.</h2>
<p>You can ignore the cancellation token if the work done inside your functions (public endpoints) is very short and quick.</p>
<p>Ignoring the token applies to your Public endpoint, not your internal functions.</p>
<p>So next time you pass around that Cancellation Token, think again,</p>
<p><em>Is this work cancellable?</em></p>
<p>Don't blindly pass around the token; it has consequences and can leave your application invalid.</p>
<h4 id="heading-references">References</h4>
<ul>
<li><a target="_blank" href="https://devblogs.microsoft.com/premier-developer/recommended-patterns-for-cancellationtoken/"><strong>Recommended patterns for CancellationToken</strong></a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[A .NET Programmer's Guide to CancellationToken]]></title><description><![CDATA[Imagine having a long-running request triggered by a user on your server.
But the user is no longer interested in the result and has navigated away from the page.
However, the server is still processing that request and utilizing resources until you ...]]></description><link>https://rahulpnath.hashnode.dev/a-net-programmers-guide-to-cancellationtoken</link><guid isPermaLink="true">https://rahulpnath.hashnode.dev/a-net-programmers-guide-to-cancellationtoken</guid><category><![CDATA[dotnet]]></category><category><![CDATA[C#]]></category><category><![CDATA[AWS]]></category><dc:creator><![CDATA[Rahul Nath]]></dc:creator><pubDate>Fri, 22 Mar 2024 03:33:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1711078376146/b140192b-6bbc-4bf5-8352-29d78811c763.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine having a long-running request triggered by a user on your server.</p>
<p>But the user is no longer interested in the result and has navigated away from the page.</p>
<p>However, the server is still processing that request and utilizing resources until you come along and implement Cancellation Tokens in the application code.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/39gIPekzpjs">https://youtu.be/39gIPekzpjs</a></div>
<p> </p>
<p>In this blog post, let's learn</p>
<ul>
<li><p>Problem with not using Cancellation Token</p>
</li>
<li><p>What is CancellationToken</p>
</li>
<li><p>Using Cancellation Token to Fix Long Running Processes</p>
</li>
<li><p>Using Cancellation Token in HTTP API Calls</p>
</li>
</ul>
<h2 id="heading-the-problem-with-not-using-cancellation-tokens">The Problem with not using Cancellation Tokens</h2>
<p>Below is a sample <code>GET</code> API endpoint. It simulates some long-running processes called in a loop a hundred times.</p>
<pre><code class="lang-csharp">app.MapGet(<span class="hljs-string">"/long-running-request"</span>, <span class="hljs-keyword">async</span> () =&gt;
    {
        <span class="hljs-keyword">var</span> randomId = Guid.NewGuid();
        <span class="hljs-keyword">var</span> results = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt;();

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">100</span>; i++)
        {
            <span class="hljs-keyword">await</span> Task.Delay(<span class="hljs-number">1000</span>);
            <span class="hljs-keyword">var</span> result = <span class="hljs-string">$"<span class="hljs-subst">{randomId}</span> - Result <span class="hljs-subst">{i}</span>"</span>;
            Console.WriteLine(result);
            results.Add(result);
        }

        <span class="hljs-keyword">return</span> Results.Ok(results);
    })
    .WithName(<span class="hljs-string">"GetAllData"</span>)
    .WithOpenApi();
</code></pre>
<p>The work is started as soon as the API endpoint is called.</p>
<p>Once invoked, if the user decides to stay at the other end of the request everything works as expected.</p>
<p>However, if the user navigates away from the API request or explicitly cancels out of the API request, the server will continue processing the request; in this case, it loops through the task a hundred times.</p>
<p>It's okay for one-off scenarios; however, if you have a busy website and the number of users increases on your website, this soon starts taking up the server resources that could otherwise be used to serve other user requests.</p>
<p>Let's learn how to fix this and put those server resources to better use.</p>
<h2 id="heading-what-is-cancellationtoken-in-net">What is CancellationToken in .NET?</h2>
<p>.NET uses Cancellation Token for cooperative cancellation of asynchronous or long-running synchronous operations.</p>
<p>Cancellation tokens are particularly useful when long-running or asynchronous operations must be canceled under certain conditions, such as in UI applications where a user might decide to cancel an ongoing operation.</p>
<p>They allow for a cooperative approach to cancellation, where the executing code periodically checks for cancellation requests and responds accordingly.</p>
<h3 id="heading-using-cancellationtoken-in-net">Using CancellationToken in .NET</h3>
<p>The CancellationToken object in itself is created and managed using the <code>CancellationTokenSource</code>.</p>
<p>The below source code shows a simple usage of the <code>CancellationToken</code>.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> source = <span class="hljs-keyword">new</span> CancellationTokenSource();
Console.WriteLine(<span class="hljs-string">"Press any key to cancel the operation..."</span>);
<span class="hljs-keyword">var</span> longRunningTask = LongRunningOperationAsync(source.Token);

Console.ReadKey();
Console.WriteLine(<span class="hljs-string">"Key Pressed"</span>);
source.Cancel();

<span class="hljs-keyword">await</span> longRunningTask;

<span class="hljs-function"><span class="hljs-keyword">async</span> Task <span class="hljs-title">LongRunningOperationAsync</span>(<span class="hljs-params">CancellationToken cancellationToken</span>)</span>
{
    <span class="hljs-keyword">try</span>
    {
        <span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">while</span> (i++ &lt; <span class="hljs-number">10</span>)
        {
            cancellationToken.ThrowIfCancellationRequested();
            <span class="hljs-comment">// Simulate some work</span>
            Console.WriteLine(<span class="hljs-string">$"Working <span class="hljs-subst">{i}</span>"</span>);
            <span class="hljs-keyword">await</span> Task.Delay(<span class="hljs-number">2000</span>, cancellationToken);
            Console.WriteLine(<span class="hljs-string">$"Completed <span class="hljs-subst">{i}</span>"</span>);
        }
    }
    <span class="hljs-keyword">catch</span> (OperationCanceledException )
    {
        Console.WriteLine(<span class="hljs-string">"User cancelled the operation"</span>);
    }
}
</code></pre>
<p>This code demonstrates how to use a cancellation token to cancel a long-running asynchronous operation in .NET.</p>
<p>It creates a <code>CancellationTokenSource</code>, starts a long-running task, and waits for a key press to cancel the operation.</p>
<p>The <code>LongRunningOperationAsync</code> method simulates work and periodically checks if cancellation has been requested, allowing for graceful cancellation handling.</p>
<p>When a key is pressed, the operation is canceled, and the program responds accordingly, providing a valid response to the calling method and the user.</p>
<p>If the user never cancels (in this case press the key), the long running operation will run to completion.</p>
<h2 id="heading-cancelling-long-running-api-requests-in-net">Cancelling Long Running API Requests in .NET</h2>
<p>So, let's fix our long-running API request, which is still on its 88th loop, computing the work we requested.</p>
<p>In ASP NET APIs, when a user makes an HTTP request, the framework automatically creates a <code>CancellationTokenSource</code> and passes the token along with the <a target="_blank" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/use-http-context?view=aspnetcore-8.0#requestaborted"><code>HttpContext</code> as the <code>ReuestAborted</code> property</a>.</p>
<p>The framework also injects it into the Controller functions if it has a <code>CancellationToken</code> property, as shown in the code below.</p>
<pre><code class="lang-csharp">app.MapGet(<span class="hljs-string">"/long-running-request"</span>, <span class="hljs-keyword">async</span> (CancellationToken cancellationToken) =&gt;
    {
        <span class="hljs-keyword">var</span> randomId = Guid.NewGuid();
        <span class="hljs-keyword">var</span> results = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt;();

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">100</span>; i++)
        {
            <span class="hljs-keyword">if</span> (cancellationToken.IsCancellationRequested)
                <span class="hljs-keyword">return</span> Results.StatusCode(<span class="hljs-number">499</span>);

            <span class="hljs-keyword">await</span> Task.Delay(<span class="hljs-number">1000</span>);
            <span class="hljs-keyword">var</span> result = <span class="hljs-string">$"<span class="hljs-subst">{randomId}</span> - Result <span class="hljs-subst">{i}</span>"</span>;
            Console.WriteLine(result);
            results.Add(result);
        }

        <span class="hljs-keyword">return</span> Results.Ok(results);
    })
</code></pre>
<p>The updated code now looks at the Cancellation Token <code>IsCancellationRequested</code> property to check if the request has been canceled.</p>
<p>It continues with the work only if the request is not cancelled, otherwise returning a different response result.</p>
<p>So now any time a user navigates away from the page or cancels the request, our long-running process will stop immediately after and stop processing that request completely.</p>
<h2 id="heading-using-cancellationtoken-for-http-requests">Using CancellationToken For HTTP Requests</h2>
<p>You can also use Cancellation Tokens when making external calls to API's or databases.</p>
<p>Let's say we have a scenario in which the user can upload files, and we decide to store them in <a target="_blank" href="https://youtu.be/3sdTztvaxhg">Amazon S3</a>.</p>
<p>Amazon Simple Storage Service (S3) is an object storage service that provides a scalable and secure storage infrastructure.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.rahulpnath.com/blog/amazon-s3-dotnet/">https://www.rahulpnath.com/blog/amazon-s3-dotnet/</a></div>
<p> </p>
<p>This could also be any other service like <a target="_blank" href="https://youtu.be/TAPERUEGhvw?si=iYxpwFNDR9lU0zLM">Azure Blob Storage</a> etc.</p>
<p>When uploading files, if they are large, it will take more time, but we can still be in a state where a user can navigate away.</p>
<p>Not handling this means we will have those large files unnecessarily uploaded to our S3 storage and our server performing those additional work for no reason.</p>
<p>We can use CancellationTokens for the rescue even in these scenarios.</p>
<pre><code class="lang-csharp">app.MapPost(<span class="hljs-string">"/upload-large-file"</span>, <span class="hljs-keyword">async</span> (
        [<span class="hljs-meta">FromForm</span>] FileUploadRequest request, 
        CancellationToken cancellationToken) =&gt;
    {
        <span class="hljs-keyword">try</span>
        {
            <span class="hljs-keyword">var</span> s3Client = <span class="hljs-keyword">new</span> AmazonS3Client();
            <span class="hljs-keyword">await</span> s3Client.PutObjectAsync(<span class="hljs-keyword">new</span> PutObjectRequest()
            {
                BucketName = <span class="hljs-string">"user-service-large-messages"</span>,
                Key = <span class="hljs-string">$"<span class="hljs-subst">{Guid.NewGuid()}</span> - <span class="hljs-subst">{request.File.FileName}</span>"</span>,
                InputStream = request.File.OpenReadStream()
            }, cancellationToken);

            <span class="hljs-keyword">return</span> Results.NoContent();
        }
        <span class="hljs-keyword">catch</span> (OperationCanceledException e)
        {
            <span class="hljs-keyword">return</span> Results.StatusCode(<span class="hljs-number">499</span>);
        }
    })
</code></pre>
<p>The <code>AmazonS3Client</code> in this scenario, which is from the S3 Nuget package takes in a CancellationToken to it's <code>PutObjectAsync</code> method.</p>
<p>Any time the user cancels the request, the SDK automatically throws the <code>OperationCanceledException</code> and cancels the upload request.</p>
]]></content:encoded></item><item><title><![CDATA[Serverless Task Automation: Task Scheduling with AWS Lambda and Amazon EventBridge]]></title><description><![CDATA[Scheduling tasks using AWS Lambda is useful for automating repetitive, time-based operations. Some common use cases include data backups, report generation, data cleanup, and periodic application maintenance.
With Amazon EventBridge Scheduler, you ca...]]></description><link>https://rahulpnath.hashnode.dev/serverless-task-automation-task-scheduling-with-aws-lambda-and-amazon-eventbridge</link><guid isPermaLink="true">https://rahulpnath.hashnode.dev/serverless-task-automation-task-scheduling-with-aws-lambda-and-amazon-eventbridge</guid><category><![CDATA[AWS]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[dotnetcore]]></category><category><![CDATA[lambda]]></category><dc:creator><![CDATA[Rahul Nath]]></dc:creator><pubDate>Thu, 22 Feb 2024 07:02:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708585205088/d7edbb7a-e64f-4472-8a28-010721b6a946.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Scheduling tasks using AWS Lambda is useful for automating repetitive, time-based operations. Some common use cases include data backups, report generation, data cleanup, and periodic application maintenance.</p>
<p>With <a target="_blank" href="https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-rule-schedule.html">Amazon EventBridge Scheduler</a>, you can specify when and how often you want a Lambda Function to run. This means you can easily set up daily, weekly, or custom cron-like schedules.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/P6yp0ByekEk">https://youtu.be/P6yp0ByekEk</a></div>
<p> </p>
<p>In this post, let's learn how to use Amazon EventBridge and set up schedules to trigger a Lambda Function built using .NET. The concepts apply to other programming languages as well.</p>
<h2 id="heading-aws-eventbridge-and-net-lambda-trigger">AWS EventBridge and .NET Lambda Trigger</h2>
<p>EventBridge Scheduler (earlier referred to as CloudWatch Events) is highly customizable and scalable with a wide set of target API operations and AWS Services.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.rahulpnath.com/blog/aws-lambda-dotnet-developer/">https://www.rahulpnath.com/blog/aws-lambda-dotnet-developer/</a></div>
<p> </p>
<p>To handle EventBridge Events from a Lambda Function we use the <code>ScheduledEvent</code> class from the <a target="_blank" href="https://www.nuget.org/packages/Amazon.Lambda.CloudWatchEvents">Amazon.Lambda.CloudWatchEvents</a> NuGet package</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-title">FunctionHandler</span>(<span class="hljs-params">ScheduledEvent input, ILambdaContext context</span>)</span>
{
    Console.WriteLine(<span class="hljs-string">$"Scheduled Task Run with <span class="hljs-subst">{JsonSerializer.Serialize(input)}</span>"</span>);
    <span class="hljs-keyword">return</span> input.Source.ToUpper();
}
</code></pre>
<p>To run the AWS Lambda Function on a schedule, create a new Lambda Trigger and configure it to use 'EventBridge (CloudWatch Events)' trigger.</p>
<p>You can create a new rule and specify the name, description, and schedule expression for the recurring schedule.</p>
<p>In EventBridge you can create two types of scheduled rules</p>
<ul>
<li><p><strong>Rate Expression</strong> → Rules that run at a regular rate.</p>
</li>
<li><p><strong>CRON Expression</strong> → Rules that run at specific times.</p>
</li>
</ul>
<p>Let's learn how to use both of these to schedule and run AWS Lambda Functions.</p>
<h2 id="heading-rate-expression-lambda-triggers">Rate Expression Lambda Triggers</h2>
<p>Rate expressions have two required fields - the value and unit - separated by white space.</p>
<pre><code class="lang-plaintext">rate(value unit)
</code></pre>
<p>Value is a positive number and unit takes <em>minute|minutes|hour|hours|day|days</em> as values.</p>
<p><img src="https://www.rahulpnath.com/content/images/2023/10/image-9.png" alt /></p>
<p>EventBridge Lambda Trigger setup with a rate expression of 1 day</p>
<p>The above screenshot shows setting up an EventBridge Lambda Trigger with a rate expression of 1 day - <em>rate(1 day).</em></p>
<h3 id="heading-eventbridge-rule-detail">EventBridge Rule Detail</h3>
<p>Once created, you can navigate to the EventBridge rules by clicking the Trigger under the Lambda Triggers list or navigating directly to the Rules section under Amazon EventBridge.</p>
<p>The below screenshot shows the Rule I created for running a Lambda Function every minute.</p>
<p><img src="https://www.rahulpnath.com/content/images/2023/10/image-11.png" alt /></p>
<p>Amazon EventBridge Rule detail that runs every minute.</p>
<p>You can further edit and customize the rule from Amazon EventBridge. You can also see the AWS Service that the rule triggers to run when it is executed.</p>
<p>The below screenshot shows that the rule triggers an AWS Lambda Function <em>lambda-schedule</em> when the rule is triggered.</p>
<p><img src="https://www.rahulpnath.com/content/images/2023/10/image-12.png" alt /></p>
<h3 id="heading-customizing-eventbridge-trigger-payload-for-lambda">Customizing EventBridge Trigger Payload For Lambda</h3>
<p>In the current Lambda Function endpoint we are using the <code>ScheduledEvent</code> type to handle the incoming Event Bridge Event.</p>
<p>However, if you need to pass in custom information as part of the schedule trigger you can do that by specifying a Constant text in JSON format.</p>
<p>This is done under the AWS EventBridge Rule by setting the Additional Settings, and setting the Configure target input to be 'Constant (JSON text)'</p>
<p><img src="https://www.rahulpnath.com/content/images/2023/10/image-13.png" alt /></p>
<p>In the above example, I have hardcoded the input to be a string input. In this case, the Lambda Function can take in a <code>string</code> as it's input type as shown below.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-title">FunctionHandler</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> input, ILambdaContext context</span>)</span>
{
  ...
}
</code></pre>
<h2 id="heading-cron-expression-lambda-triggers">CRON Expression Lambda Triggers</h2>
<p>Rate Expressions allows you to specify how often you want a Rule to trigger. However, when you need more fine-grained control over when a rule should trigger you can use <a target="_blank" href="https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-cron-expressions.html">Cron Expression</a>.</p>
<p>Cron expressions have six required fields, which are separated by white space.</p>
<p><img src="https://www.rahulpnath.com/content/images/2023/10/image-14.png" alt /></p>
<p>Cron Expression example Lambda Trigger using AWS EventBridge</p>
<p>The above screenshot sets up a CRON job to run the function every minute between 4-5 a.m. (UTC) on a Monday and Tuesday.</p>
<pre><code class="lang-plaintext">cron(0/1 4-5 ? * MON-TUE *)
</code></pre>
<p>All CRON jobs are set in UTC time, however, if you navigate to the CRON job rule in EventBridge, you can see the time in both UTC and local time zone.</p>
<p><img src="https://www.rahulpnath.com/content/images/2023/10/image-15.png" alt /></p>
<p>CRON AWS EventBridge Rule detail showing the Event Schedule and the Next 10 trigger dates.</p>
<p>The Event Schedule shows the next 10 upcoming triggers in both UTC and Local time to ensure the CRON job is set as expected.</p>
]]></content:encoded></item><item><title><![CDATA[Learn How to Easily Integrate Lambda Annotations and Other AWS Services]]></title><description><![CDATA[The Lambda Annotations Framework is a programming model that makes it easier to build AWS Lambda Functions using .NET.
In previous blog posts, we learned how to get started using the Lambda Annotations Framework, build a CRUD API Endpoint, and set up...]]></description><link>https://rahulpnath.hashnode.dev/learn-how-to-easily-integrate-lambda-annotations-and-other-aws-services</link><guid isPermaLink="true">https://rahulpnath.hashnode.dev/learn-how-to-easily-integrate-lambda-annotations-and-other-aws-services</guid><category><![CDATA[dotnet]]></category><category><![CDATA[AWS]]></category><category><![CDATA[lambda]]></category><dc:creator><![CDATA[Rahul Nath]]></dc:creator><pubDate>Mon, 19 Feb 2024 03:58:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708314880871/44ce2ae2-7391-46ac-a94b-2e523f35a9cc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The <a target="_blank" href="https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.Annotations/README.md?ref=rahulpnath.com">Lambda Annotations Framework</a> is a programming model that makes it easier to build AWS Lambda Functions using .NET.</p>
<p>In previous blog posts, we learned how to get started using the Lambda Annotations Framework, build a CRUD API Endpoint, and set up Dependency Injection when creating Lambda Functions using the Annotations Framework.</p>
<ul>
<li><p><a target="_blank" href="https://www.rahulpnath.com/blog/aws-lambda-annotation-framework/">Learn How AWS Lambda Annotations Framework Makes API Gateway Integration Easy</a></p>
</li>
<li><p><a target="_blank" href="https://www.rahulpnath.com/blog/lambda-annotation-framework-crud-api/">Serverless API Development Made Easy: Using AWS Lambda Annotations for CRUD</a></p>
</li>
<li><p><a target="_blank" href="https://www.rahulpnath.com/blog/lambda-annotations-dependency-injection/">How To Set Up Dependency Injection in Lambda Functions Using Annotations Framework</a></p>
</li>
</ul>
<p>In all the above scenarios we used the Annotations Framework along with building Lambda Functions for the API Gateway.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/RhGQSTVhfls">https://youtu.be/RhGQSTVhfls</a></div>
<p> </p>
<p>However, you can also use the Annotations Framework when building Lambda Functions that integrate with other AWS Services.</p>
<p>In this blog post, let's see a quick example of using the Lambda Annotations Framework when building a Lambda Function with Amazon S3 service.</p>
<p>For E.g., we need to run a Lambda Function every time a new file is uploaded to an Amazon S3 bucket and have the function code read the file and process it. This could be an image post-processing Lambda Function, a report ingestion service, a bulk data upload service, etc.</p>
<h2 id="heading-aws-lambda-and-amazon-s3-integration">AWS Lambda and Amazon S3 Integration</h2>
<p>Amazon Simple Storage Service (S3) is an object storage service that allows you to store any data.</p>
<p>Any time an object is created or modified in S3, it raises event notifications. We can use these notifications in specific scenarios to perform additional business logic or application processing.</p>
<p>To learn more about this in detail check my Amazon S3 and AWS Lambda Triggers in .NET, blog post.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.rahulpnath.com/blog/amazon-s3-lambda-triggers-dotnet/">https://www.rahulpnath.com/blog/amazon-s3-lambda-triggers-dotnet/</a></div>
<p> </p>
<p>Lambda Functions that integrate with Amazon S3 services, take in a <code>S3Event</code> class type, to bind to the Amazon S3 event.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">FunctionHandler</span>(<span class="hljs-params">S3Event evnt, ILambdaContext context</span>)</span>
{
  ...
  <span class="hljs-keyword">var</span> file = <span class="hljs-keyword">await</span> <span class="hljs-keyword">this</span>.S3Client
      .GetObjectAsync(s3Event.Bucket.Name, s3Event.Object.Key);
  ...
}
</code></pre>
<p>This happens by default using the basic Lambda JSON Serializer and there is nothing specific that the Annotations Framework can provide us here.</p>
<p>However, the features that are useful from the Annotations Framework when integrating with other AWS Services are Dependency Injection and Serverless Template for easy deployment.</p>
<h2 id="heading-dependency-injection-for-lambda-functions">Dependency Injection For Lambda Functions</h2>
<p>Lambda Annotations Framework provides an easy out-of-the-box mechanism to set up and work with .NET Dependency Injection framework.</p>
<p>Instead of hard-coding the instance creation in the Function constructor, you can inject in the dependencies that the Function needs using the Function constructor or the Handler Function.</p>
<p>To enable the Annotations Framework we need to apply the <code>LambdaFunction</code> attribute to the Function Handler as shown below.</p>
<p>The below code shows examples of injecting the <code>IAmazonS3</code> instance both using the constructor and also using the Function Handler.</p>
<pre><code class="lang-plaintext">public Function(IAmazonS3 s3Client)
{
    S3Client = s3Client;
}

[LambdaFunction(ResourceName = "MyLambdaFunction")]
public async Task FunctionHandler(
  [FromServices] IImageServices imageServices, S3Event evnt, ILambdaContext context)
{
  ...
}
</code></pre>
<p>To inject via the FunctionHandler method, we need to add <code>FromServices</code> attribute, to tell Annotations Framework to resolve the type from the DI container.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">Amazon.Lambda.Annotations.LambdaStartup</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Startup</span>
{
  <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span>(<span class="hljs-params">IServiceCollection services</span>)</span>
  {
      services.AddAWSService&lt;Amazon.S3.IAmazonS3&gt;();
  }
}
</code></pre>
<h2 id="heading-serverless-template-file-for-deployment">Serverless Template File For Deployment</h2>
<p>Enable the Annotations Framework also generates the CloudFormation template file, which can be used to automate resource deployment.</p>
<p>In this scenario, since we are integrating with Amazon S3, we can further define the S3 bucket and also the required triggers to wire up the S3 event notification to trigger the Lambda Function.</p>
<pre><code class="lang-json"><span class="hljs-string">"Bucket"</span>: {
      <span class="hljs-attr">"Type"</span>: <span class="hljs-string">"AWS::S3::Bucket"</span>,
      <span class="hljs-attr">"Properties"</span>: {
        <span class="hljs-attr">"BucketName"</span>: {
          <span class="hljs-attr">"Ref"</span>: <span class="hljs-string">"BucketName"</span>
        },
        <span class="hljs-attr">"NotificationConfiguration"</span>: {
          <span class="hljs-attr">"LambdaConfigurations"</span>: [
            {
              <span class="hljs-attr">"Event"</span>: <span class="hljs-string">"s3:ObjectCreated:*"</span>,
              <span class="hljs-attr">"Filter"</span>: {
                <span class="hljs-attr">"S3Key"</span>: {
                  <span class="hljs-attr">"Rules"</span>: [
                    {
                      <span class="hljs-attr">"Name"</span>: <span class="hljs-string">"prefix"</span>,
                      <span class="hljs-attr">"Value"</span>: <span class="hljs-string">"test/"</span>
                    },
                    {
                      <span class="hljs-attr">"Name"</span>: <span class="hljs-string">"suffix"</span>,
                      <span class="hljs-attr">"Value"</span>: <span class="hljs-string">".txt"</span>
                    }
                  ]
                }
              },
              <span class="hljs-attr">"Function"</span>: {
                <span class="hljs-attr">"Fn::GetAtt"</span>: [
                  <span class="hljs-string">"MyLambdaFunction"</span>,
                  <span class="hljs-string">"Arn"</span>
                ]
              }
            }
          ]
        }
      }
    },
    <span class="hljs-string">"S3InvokeLambdaPermission"</span>: {
      <span class="hljs-attr">"Type"</span>: <span class="hljs-string">"AWS::Lambda::Permission"</span>,
      <span class="hljs-attr">"Properties"</span>: {
        <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"lambda:InvokeFunction"</span>,
        <span class="hljs-attr">"FunctionName"</span>: {
          <span class="hljs-attr">"Ref"</span>: <span class="hljs-string">"MyLambdaFunction"</span>
        },
        <span class="hljs-attr">"Principal"</span>: <span class="hljs-string">"s3.amazonaws.com"</span>,
        <span class="hljs-attr">"SourceArn"</span>: {
          <span class="hljs-attr">"Fn::Sub"</span>: <span class="hljs-string">"arn:aws:s3:::${BucketName}"</span>
        }
      }
    },
    <span class="hljs-string">"LambdaRole"</span>: {
      <span class="hljs-attr">"Type"</span>: <span class="hljs-string">"AWS::IAM::Role"</span>,
      <span class="hljs-attr">"Properties"</span>: {
        <span class="hljs-attr">"AssumeRolePolicyDocument"</span>: {
          <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
          <span class="hljs-attr">"Statement"</span>: [
            {
              <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
              <span class="hljs-attr">"Principal"</span>: {
                <span class="hljs-attr">"Service"</span>: <span class="hljs-string">"lambda.amazonaws.com"</span>
              },
              <span class="hljs-attr">"Action"</span>: [
                <span class="hljs-string">"sts:AssumeRole"</span>
              ]
            }
          ]
        },
        <span class="hljs-attr">"Path"</span>: <span class="hljs-string">"/"</span>,
        <span class="hljs-attr">"ManagedPolicyArns"</span>: [
          <span class="hljs-string">"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"</span>
        ],
        <span class="hljs-attr">"Policies"</span>: [
          {
            <span class="hljs-attr">"PolicyName"</span>: <span class="hljs-string">"s3"</span>,
            <span class="hljs-attr">"PolicyDocument"</span>: {
              <span class="hljs-attr">"Statement"</span>: [
                {
                  <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
                  <span class="hljs-attr">"Action"</span>: [
                    <span class="hljs-string">"s3:Get*"</span>
                  ],
                  <span class="hljs-attr">"Resource"</span>: [
                    {
                      <span class="hljs-attr">"Fn::Sub"</span>: <span class="hljs-string">"arn:aws:s3:::${BucketName}"</span>
                    },
                    {
                      <span class="hljs-attr">"Fn::Sub"</span>: <span class="hljs-string">"arn:aws:s3:::${BucketName}/*"</span>
                    }
                  ]
                }
              ]
            }
          }
        ]
      }
    }
</code></pre>
<p>The above template is in addition to the automatically generated template for the Lambda Function.</p>
<p>It sets up the S3 bucket (<code>Bucket</code>), the Lambda IAM Role (<code>LambdaRole</code>) giving it permission to read from the S3 bucket and also permission for the S3 bucket to invoke the Lambda Function (<code>S3InvokeLambdaPermission</code>).</p>
<p>This makes it easy to deploy the required resources and permissions together along with changes to the Lambda Function.</p>
<p>Similar to Amazon S3, you can also use the same approach to integrate with other AWS Services like SQS, SNS, DynamoDB, etc.</p>
]]></content:encoded></item><item><title><![CDATA[How To Effectively Manage Sensitive Information in AWS Lambda: Powertools Parameters]]></title><description><![CDATA[When building Lambda Functions, we often need to store configuration and sensitive information.
AWS Provides different services like Parameter Store, Secrets Manager, etc., to store sensitive information.
The AWS Lambda Powertools library makes it ea...]]></description><link>https://rahulpnath.hashnode.dev/how-to-effectively-manage-sensitive-information-in-aws-lambda-powertools-parameters</link><guid isPermaLink="true">https://rahulpnath.hashnode.dev/how-to-effectively-manage-sensitive-information-in-aws-lambda-powertools-parameters</guid><category><![CDATA[AWS]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[lambda]]></category><dc:creator><![CDATA[Rahul Nath]]></dc:creator><pubDate>Fri, 16 Feb 2024 02:01:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708048856338/41210f00-dec8-4a66-84da-98d41363dd48.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When building Lambda Functions, we often need to store configuration and sensitive information.</p>
<p>AWS Provides different services like Parameter Store, Secrets Manager, etc., to store sensitive information.</p>
<p>The AWS Lambda Powertools library makes it easy to work with these different services and retrieve one of their multiple parameter values.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=3Mdbaf4ox78&amp;ab_channel=RahulNath">https://www.youtube.com/watch?v=3Mdbaf4ox78&amp;ab_channel=RahulNath</a></div>
<p> </p>
<p>In this blog post, let’s learn how to get started using the Lambda Powertools Parameters NuGet package, use it when building Lambda Functions, and connect quickly to Parameter Store and Secrets Manager using the library package.</p>
<h2 id="heading-aws-powertools-parameters-package">AWS Powertools Parameters Package</h2>
<p>Powertools Parameters utility is available as a NuGet package. To get started using it from the application, install <a target="_blank" href="https://www.nuget.org/packages?q=AWS.Lambda.Powertools.Parameters">AWS.Lambda.Powertools.Parameters</a> NuGet package.</p>
<p>Once installed, we can use it to integrate with the various AWS Services to manage secrets and configuration.</p>
<h2 id="heading-aws-lambda-amp-parameters-store">AWS Lambda &amp; Parameters Store</h2>
<p>AWS Parameter Store is a centralized, secure store for your application configuration.</p>
<p>Parameter Store, a part of AWS Systems Manager, provides secure storage for application configuration and secret data. As parameter values, you can store passwords, database strings, Amazon Machine Image (AMI) IDs, API Keys, etc.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.rahulpnath.com/blog/aws-parameter-store/">https://www.rahulpnath.com/blog/aws-parameter-store/</a></div>
<p> </p>
<p>Parameter Store makes decoupling your code from configuration easy and acts as version control for your configuration data.</p>
<h3 id="heading-retrieving-secrets-from-parameter-store">Retrieving Secrets From Parameter Store</h3>
<p>You can either retrieve one or multiple secrets at a time from the Parameter Store.</p>
<p>The Powertools Parameter utility provides the <code>ParametersManager.SsmProvider</code> utility class to interact with the Parameter Store.</p>
<pre><code class="lang-csharp">    <span class="hljs-keyword">var</span> <span class="hljs-keyword">value</span> = <span class="hljs-keyword">await</span> ParametersManager.SsmProvider.GetAsync(<span class="hljs-string">"/Value1"</span>);
    <span class="hljs-keyword">var</span> multiple = <span class="hljs-keyword">await</span> ParametersManager.SsmProvider
         .GetMultipleAsync(<span class="hljs-string">"/weather-app/"</span>);
</code></pre>
<p>Use <code>GetAsync</code> method to retrieve one value and the <code>GetMultipleAsync</code> method to return multiple values given a key prefix.</p>
<p>In the example about, <code>value</code> represents the value of the key '<em>/Value</em>', and multiple has all the parameter key values that start with '<em>/weather-app</em>'.</p>
<h3 id="heading-lambda-permissions-for-parameter-store">Lambda Permissions for Parameter Store</h3>
<p>For the Lambda Function to retrieve values from the Parameter Store, it needs appropriate permissions.</p>
<p>Let's update the IAM permission of our Lambda function and add the below policy to give it permission to retrieve the required keys.</p>
<p>To retrieve one parameter, we need the <code>ssm:GetParameter</code> and for multiple <code>ssm:GetParametersByPath</code> Action permissions. The below policy provides all actions starting with 'ssm:GetParameter', which is denoted by the '*' at the end.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"PolicyName"</span>: <span class="hljs-string">"OrderApiParametersStorePolicy"</span>,
  <span class="hljs-attr">"PolicyDocument"</span>: {
    <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-attr">"Statement"</span>: [
      {
        <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
        <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"ssm:GetParameter*"</span>,
        <span class="hljs-attr">"Resource"</span>: [
          <span class="hljs-string">"arn:aws:ssm:ap-southeast-2:189107071895:parameter/Value1"</span>,
          <span class="hljs-string">"arn:aws:ssm:ap-southeast-2:189107071895:parameter/weather-app/*"</span>
        ]
      }
    ]
  }
}
</code></pre>
<h3 id="heading-transforming-parameter-store-values">Transforming Parameter Store Values</h3>
<p>The Parameters utility supports transforming of values stored in Parameter Store.</p>
<p>For e.g., the parameter '/my-configuration' is stored as JSON in the Parameter Store.</p>
<p>When retrieving the value, we can use the <code>WithTransformation</code> and specify to use JSON Transformation. This allows us to automatically deserialize the configuration to a custom type that we specify (in this case <code>MyConfiguration</code>)</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> myConfiguration = <span class="hljs-keyword">await</span> ParametersManager.SsmProvider.WithTransformation(Transformation.Json)
     .GetAsync&lt;MyConfiguration&gt;(<span class="hljs-string">"/my-configuration"</span>);
...
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">MyConfiguration</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Secret { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Url { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<p>The utility also supports <a target="_blank" href="https://docs.powertools.aws.dev/lambda/dotnet/utilities/parameters/#transform-values">Base64 transformation</a>.</p>
<h2 id="heading-aws-lambda-amp-secrets-manager">AWS Lambda &amp; Secrets Manager</h2>
<p>AWS Secrets Manager provides a centralized store to manage your application secrets.</p>
<p>Secrets can be information like passwords, credentials, connection strings, API keys, etc. Secrets Manager helps you protect access to your IT resources and data by enabling you to rotate and manage access to your secrets.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.rahulpnath.com/blog/aws-secrets-manager/">https://www.rahulpnath.com/blog/aws-secrets-manager/</a></div>
<p> </p>
<h3 id="heading-retrieving-secrets-from-secrets-manager">Retrieving Secrets From Secrets Manager</h3>
<p>The Parameters utility provides <code>ParametersManager.SecretsProvider</code> to retrieve secrets from the Secrets Manager.</p>
<p>Secrets Manager currently supports only retrieving one secret value at a time. Trying to use the <code>GetMultipleAsync</code> method will throw an exception at runtime.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> secret1 = <span class="hljs-keyword">await</span> ParametersManager.SecretsProvider
       .GetAsync(<span class="hljs-string">"weather-app/secret1"</span>);
</code></pre>
<h3 id="heading-lambda-permissions-for-secrets-manager">Lambda Permissions for Secrets Manager</h3>
<p>The Lambda Function requires <code>secretsmanager:GetSecretValue</code> permission on the Secret Values to return them successfully.</p>
<p>Let's update the IAM Permission also to include the required permission to retrieve the Secret from Secrets Manager, as shown below.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"PolicyName"</span>: <span class="hljs-string">"OrderApiSecretsStorePolicy"</span>,
  <span class="hljs-attr">"PolicyDocument"</span>: {
    <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-attr">"Statement"</span>: [
      {
        <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
        <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"secretsmanager:GetSecretValue"</span>,
        <span class="hljs-attr">"Resource"</span>: [
          <span class="hljs-string">"arn:aws:secretsmanager:ap-southeast-2:189107071895:secret:weather-app/secret1*"</span>
        ]
      }
    ]
  }
}
</code></pre>
<h2 id="heading-dynamodb-provider">DynamoDB Provider</h2>
<p>The Parameters utility also supports using the DynamoDB table as a source of Parameter key values.</p>
<p>You can use the <code>ParametersManager.DynamoDBProvider</code> to interact with the configurated DynamoDB table to return key-value pair.</p>
<p>I'll leave that exercise to you to explore and use. You can read more about it <a target="_blank" href="https://docs.powertools.aws.dev/lambda/dotnet/utilities/parameters/#dynamodb-provider">here in the documentation</a>key-value.</p>
]]></content:encoded></item><item><title><![CDATA[Why Should You Care About Lambda Lifecycle As A .NET Developer?]]></title><description><![CDATA[I made a terrible mistake when I first started using AWS Lambda Functions.
I used an instance variable in my .NET class to store the Function state and reset it in the class Constructor code. I was expecting the state to reset for each function invoc...]]></description><link>https://rahulpnath.hashnode.dev/why-should-you-care-about-lambda-lifecycle-as-a-net-developer</link><guid isPermaLink="true">https://rahulpnath.hashnode.dev/why-should-you-care-about-lambda-lifecycle-as-a-net-developer</guid><category><![CDATA[AWS]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[lambda]]></category><dc:creator><![CDATA[Rahul Nath]]></dc:creator><pubDate>Fri, 16 Feb 2024 01:58:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1707963744243/462bdb6e-08a6-40f1-ae3b-00cad5cf7c60.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I made a terrible mistake when I first started using AWS Lambda Functions.</p>
<p>I used an instance variable in my .NET class to store the Function state and reset it in the class Constructor code. I was expecting the state to reset for each function invocation.</p>
<p>But, the Function state was not getting reset on each invocation.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/DT-j7OlEAFY">https://youtu.be/DT-j7OlEAFY</a></div>
<p> </p>
<p>Understanding the AWS Lambda Lifecycle is essential when building Lambda Functions.</p>
<p>It would be best to avoid certain things when writing your Function code; we will go over those and why in this article.</p>
<h2 id="heading-aws-lambda-function-lifecycle">AWS Lambda Function Lifecycle</h2>
<p>AWS Lambda provides a secure and isolated runtime environment for our function code. This execution environment manages the resources required to run our Function and also provides lifecycle support for the Function’s runtime.</p>
<p>The Lambda Lifecycle mainly consists of three phases:</p>
<ul>
<li><p><strong>Init Phase →</strong> Limited to 10 seconds. Starts all extension, runs Function’s static code, etc</p>
</li>
<li><p><strong>Invoke Phase →</strong> Invokes the Function handler configured when setting up Lambda Function and waits for the duration of the Functions timeout.</p>
</li>
<li><p><strong>Shutdown Phase →</strong> Limited to 2 seconds.</p>
</li>
</ul>
<p><img src="https://www.rahulpnath.com/content/images/2023/02/image.png" alt="Diagram showing the different lifecycle phases of a Lambda Function. (From the AWS Docs)" /></p>
<p>You can read more about each phase and the related activities in the <a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtime-environment.html#runtimes-lifecycle">official documentation here</a>.</p>
<h2 id="heading-lambda-lifecycle-and-net-functions">Lambda Lifecycle and .NET Functions</h2>
<p>The Lambda Lifecycle affects the way we write our Function code.</p>
<p>Especially when using .NET, where you define the Function code within a Class. In .NET, you can define instance variables - properties at your Class level scope.</p>
<p>Let’s consider a simple AWS Lambda Function code, as shown below.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Function</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> Count { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Function</span>(<span class="hljs-params"></span>)</span>
    {
        Count = <span class="hljs-number">0</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-title">FunctionHandler</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> input, ILambdaContext context</span>)</span>
    {
        Count++;
        <span class="hljs-keyword">return</span> <span class="hljs-string">$"<span class="hljs-subst">{input}</span> - <span class="hljs-subst">{Count}</span>"</span>;
    }
}
</code></pre>
<p>We have a class <code>Function</code> where we have the Lambda Function Handler (<code>FunctionHandler</code>) which takes in string input. The <code>Function</code> class also defines a <code>Count</code> property that is initialized inside the Class constructor.</p>
<p>Since the <code>FunctionHandler</code> increments the <code>Count</code> property every time it’s called, the return value depends on whether you are invoking the Function on the same instance or different instances</p>
<p>I was expecting the <code>Count</code> property to reset every time the Lambda Function was invoked because of the initialization code in the constructor. (Now, in my original scenario where I encountered this problem, I was doing much more than just incrementing a counter.)</p>
<p>But, like I said, it wasn’t. Why?</p>
<h3 id="heading-net-constructor-and-lambda-init-phase">.NET Constructor and Lambda Init Phase</h3>
<p>For .NET Functions, a new instance of the Function class gets created in the Init Phase. This instance is then stored and used in the Invoke phases, where all it does is pass on the request to the Function handler.</p>
<p>For any new Invoke requests, Lambda uses any free instances and invokes the function method on it.</p>
<blockquote>
<p><em>AWS Lambda creates new instances of .NET class in the Init phase and reuses the instance in the Invoke Phase.</em></p>
</blockquote>
<p>This means any initialization code in the class constructor is only called once when the instance is created. Any state maintained at the class/instance level must be reset appropriately inside the Function handler method.</p>
<p>So in our example before, if we invoke the Lambda Function multiple times, it keeps incrementing the <code>Count</code> property and returns the value accordingly.</p>
<h3 id="heading-what-triggers-new-net-instances-to-be-created">What Triggers New .NET Instances To Be Created?</h3>
<p>All existing instances are removed whenever you change your function code and deploy the changes to AWS Lambda.</p>
<p>Any new requests coming to the AWS Lambda will create a new instance with the newly deployed code.</p>
<p>New instances are also automatically created when Lambda receives more requests than what it can process.</p>
<p>Let’s simulate the Function doing some work by adding a Thread sleep to our code, as shown below.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-title">FunctionHandler</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> input, ILambdaContext context</span>)</span>
{
    Count++;
    Thread.Sleep(<span class="hljs-number">1000</span> * <span class="hljs-number">5</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-string">$"<span class="hljs-subst">{input}</span> - <span class="hljs-subst">{Count}</span>"</span>;
}
</code></pre>
<p>The Function now takes 5 seconds to respond once invoked.</p>
<p>If we make two parallel requests to our Lambda Function, the Lambda Runtime will automatically create 2 instances to serve the requests simultaneously. This behavior changes based on the <a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-concurrency.html">Lambda concurrency setting</a>.</p>
<p>To simulate two parallel requests, you can make requests from Visual Studio, AWS Console, or the command line.</p>
<p>Below is how you can use the aws cli to invoke a function in PowerShell.</p>
<pre><code class="lang-powershell">aws lambda invoke -<span class="hljs-literal">-function</span><span class="hljs-literal">-name</span> lambda<span class="hljs-literal">-lifecycle</span> -<span class="hljs-literal">-cli</span><span class="hljs-literal">-binary</span><span class="hljs-literal">-format</span> raw<span class="hljs-operator">-in</span><span class="hljs-literal">-base64</span><span class="hljs-literal">-out</span> -<span class="hljs-literal">-payload</span> <span class="hljs-string">'\\"Console\\"'</span> response.json &amp;&amp; <span class="hljs-built_in">cat</span> .\\response.json
</code></pre>
<p>If you make two parallel calls after freshly deploying the Lambda Function with the above code, you will see that both invocations return the Count as 1. Since Lambda creates multiple instances of the class to handle both requests happening at the same time.</p>
<h3 id="heading-what-to-put-in-the-net-constructor">What to Put In The .NET Constructor?</h3>
<p>It would be best if you didn’t depend on when and how Lambda decides to create new instances. Your code should work regardless of how the internals of Lambda works.</p>
<p>So to ensure this, make sure your Function classes are stateless, and you don't depend on any particular class level variables within your Function Handler.</p>
<p>This brings us to the obvious question, What can I put in the .NET Constructor?</p>
<p>I typically have any connections to databases or external services, instantiated in the class constructor, and reuse them inside the Function handler, provided these connections are stateless and thread-safe.</p>
<p>Some examples are DynamoDB connections, Parameter Store connections, etc.</p>
<p>For anything else I prefer to have the variables scoped to the Function handler.</p>
]]></content:encoded></item><item><title><![CDATA[How To Set Up Dependency Injection in Lambda Functions Using Annotations Framework]]></title><description><![CDATA[The Lambda Annotations Framework provides a programming model for .NET developers to create AWS Lambda Functions.
In previous posts, we learned how to get started with the Annotations Framework and also how to build a CRUD API using the Annotations F...]]></description><link>https://rahulpnath.hashnode.dev/how-to-set-up-dependency-injection-in-lambda-functions-using-annotations-framework</link><guid isPermaLink="true">https://rahulpnath.hashnode.dev/how-to-set-up-dependency-injection-in-lambda-functions-using-annotations-framework</guid><category><![CDATA[AWS]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[lambda]]></category><dc:creator><![CDATA[Rahul Nath]]></dc:creator><pubDate>Tue, 13 Feb 2024 03:18:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1707794156507/603d4825-8e78-42e0-a40a-fa702808720b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The Lambda Annotations Framework provides a programming model for .NET developers to create AWS Lambda Functions.</p>
<p>In previous posts, we learned <a target="_blank" href="https://www.rahulpnath.com/blog/aws-lambda-annotation-framework/">how to get started with the Annotations Framework</a> and also <a target="_blank" href="https://www.rahulpnath.com/blog/lambda-annotation-framework-crud-api/">how to build a CRUD API</a> using the Annotations Framework.</p>
<p>We saw how the Annotations Framework makes the development experience with Lambda Functions very similar to building APIs using ASP NET Core Framework.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=FPaznlIY02s&amp;ab_channel=RahulNath">https://www.youtube.com/watch?v=FPaznlIY02s&amp;ab_channel=RahulNath</a></div>
<p> </p>
<p>Another feature that the Annotations Framework provides is support for Dependency Injection. This takes the development experience with Lambda Functions a level up.</p>
<p>In this post let’s learn <a target="_blank" href="https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.Annotations/README.md#dependency-injection-integration">how to setup Dependency Injection</a> when building Lambda Functions using the Annotations Framework.</p>
<h2 id="heading-setting-up-di-in-net-lambda-annotations">Setting Up DI In .NET Lambda Annotations</h2>
<p>Lamba Annotations provides the <code>LambdaStartup</code> attribute that can be applied on any class in the Lambda Function Project.</p>
<p>The class must have a method <code>ConfigureServices</code> taking in the <code>IServiceCollection</code> to set up the Dependency Injection container.</p>
<p>Below I have the <code>Startup</code> class which has the method specified and the <code>LambdaStartup</code> attribute applied.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">LambdaStartup</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Startup</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span>(<span class="hljs-params">IServiceCollection services</span>)</span>
    {
        services.AddTransient&lt;IDependency, MyDependency&gt;();
    }
}
</code></pre>
<p>The function sets up and registers the different dependencies in the service collection.</p>
<h2 id="heading-injecting-dependencies-in-net-lambda-function">Injecting Dependencies in .NET Lambda Function</h2>
<p>The Annotations framework supports injecting dependencies through the constructor or through the Lambda Function entry point itself.</p>
<h3 id="heading-constructor-level-di-in-lambda-function">Constructor Level DI in Lambda Function</h3>
<p>To inject the dependency through the constructor is very similar to how we do DI in .NET. All we need to do is specify the interface dependency in the constructor as shown below.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IMyDependency _myDependencyCtor;

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Function</span>(<span class="hljs-params">IMyDependency myDependencyCtor</span>)</span>
{
    _myDependencyCtor = myDependencyCtor;
}
</code></pre>
<p>The above code injects the <code>IDependency</code> we registered in the <code>Startup</code> class to the Function class.</p>
<p>We can store this as a class-level property and use it in our Lambda Function code.</p>
<h3 id="heading-function-level-di-in-lambda-function">Function Level DI in Lambda Function</h3>
<p>You can also inject a dependency through the Function parameters by adding the <code>FromServices</code> attribute.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">LambdaFunction</span>]
[<span class="hljs-meta">HttpApi(LambdaHttpMethod.Get, <span class="hljs-meta-string">"/add/{a}/{b}"</span>)</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;<span class="hljs-keyword">string</span>&gt; <span class="hljs-title">Add</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b, ILambdaContext lambdaContext, [FromServices] IDependency scopedDependencyFunc</span>)</span>
{
    <span class="hljs-keyword">var</span> returnValues = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt;
    {
        _scopedDependencyCtor.Test(<span class="hljs-string">"Constructor"</span>),
        scopedDependencyFunc.Test(<span class="hljs-string">"Function"</span>),
        (a + b).ToString()
    };

    <span class="hljs-keyword">return</span> returnValues;
}
</code></pre>
<p>The annotation framework uses this attribute to determine that it's a dependency to be resolved from the DI ServiceCollection container and uses it to resolve and pass the appropriate values when invoking the Function code.</p>
<h2 id="heading-di-service-lifetimes-and-lambda-lifecycle">DI Service Lifetimes and Lambda Lifecycle</h2>
<p>The ServiceCollection supports three lifetimes when registering dependencies.</p>
<ul>
<li><p>Transient → New instance created every request</p>
</li>
<li><p>Scoped → Instance created once per client request</p>
</li>
<li><p>Singleton → Single instance of dependency created</p>
</li>
</ul>
<p>However, this is dependent on the lifecycle of the Lambda Functions.</p>
<p>The Lambda Function class is created only once, during the Init phase of the Lambda lifecycle. Once the instance is created it is reused for the same calls handled by the Lambda instance.</p>
<p>💡</p>
<p><em>The Lambda Function class is instantiated only once when a new Lambda instance is created. The same class instance is reused to call the function entry point on the same Lambda instance.</em></p>
<p>It's up to the AWS Lambda infrastructure to decide when to create a new Lambda instance. When the function is just deployed, the Lambda runtime wipes out all existing instances and creates new instances for subsequent requests.</p>
<p>Learn more about Lambda Lifecycle in the below blog post.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.rahulpnath.com/blog/lambda-lifecycle-and-net/">https://www.rahulpnath.com/blog/lambda-lifecycle-and-net/</a></div>
<p> </p>
<p>The lifetime scope of the dependencies injected via the Service collection is dependent on the Lambda instance.</p>
<p>For example, a singleton instance is only singleton in the context of one Lambda instance. If you have multiple parallel calls coming to your API Gateway, Lambda might decide to spin up multiple Lambda instances and each of them will have its own Singleton instance.</p>
<p>💡</p>
<p>Avoid depending on Singleton instances to maintain state or share data across multiple invocations.</p>
<p>All application state information must be managed outside of Lambda - like in a database or an external Cache service etc.</p>
<h2 id="heading-under-the-hoods-of-di-in-lambda-annotation-framework">Under the Hoods of DI in Lambda Annotation Framework</h2>
<p>As we have seen previously, the annotations framework generates a wrapper class around our actual Function code class for each <code>LambdaFunction</code> attributed function.</p>
<p>This wrapper class is where all of the boilerplate code is auto-generated using .NET Source Generators.</p>
<p>Below you can see in the generated class, it creates a new instance of the .NET <code>ServiceCollection</code> which is the DI container where we are registering the dependencies in the <code>Startup</code> class.</p>
<p><img src="https://www.rahulpnath.com/content/images/2023/08/image-2.png" alt /></p>
<p>The Lambda generated class by the annotation framework as seen using JetBrains dotpeek.</p>
<p>The actually <code>Function</code> class is registered as a Singleton instance in the DI container.</p>
<p>If needed you can override this to a different lifetime scope inside the <code>Startup</code> class. Since the last registration wins, anything in the Startup class ConfigureServices method will override the auto-generated registration set up in the wrapper.</p>
<p>The wrapper Function code uses the ServiceCollection instance to resolve the actual function class and any Function parameter dependencies.</p>
<p>The generated function class wraps around the actual function and invokes the function we have written passing in the appropriate dependencies.</p>
<p>You can find the full source code <a target="_blank" href="https://github.com/rahulpnath/youtube-samples/tree/main/LambdaAnnotationSample/LambdaAnnotationSample.FromScratch">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[5 Ways To Query Data From Amazon DynamoDB using .NET]]></title><description><![CDATA[Querying is an essential operation in DynamoDB.
It allows you to filter and select items in your database based on your application and user needs.
When moving over to DynamoDB from more traditional relational databases like SQL Server, you must unde...]]></description><link>https://rahulpnath.hashnode.dev/5-ways-to-query-data-from-amazon-dynamodb-using-net</link><guid isPermaLink="true">https://rahulpnath.hashnode.dev/5-ways-to-query-data-from-amazon-dynamodb-using-net</guid><category><![CDATA[AWS]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[DynamoDB]]></category><dc:creator><![CDATA[Rahul Nath]]></dc:creator><pubDate>Fri, 09 Feb 2024 05:16:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1707455565068/f5f9beb5-4df4-4942-b3b1-e566e61db172.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Querying is an essential operation in DynamoDB.</p>
<p>It allows you to filter and select items in your database based on your application and user needs.</p>
<p>When moving over to DynamoDB from more traditional relational databases like SQL Server, you must understand the different ways you can retrieve data in DynamoDB.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/iv6OKueqBd4">https://youtu.be/iv6OKueqBd4</a></div>
<p> </p>
<p>In this article, let’s explore the different ways you can query data from Amazon DynamoDB when building applications using .NET. We will learn</p>
<ul>
<li><p>Loading Specific Items</p>
</li>
<li><p>Querying by Partition Key</p>
</li>
<li><p>Querying by Partition and Sort Key</p>
</li>
<li><p>Using Low-level .NET SDK API For Queries</p>
</li>
<li><p>Filtering Data By Other Properties</p>
</li>
</ul>
<h2 id="heading-quick-recap-of-dynamodb">Quick Recap of DynamoDB</h2>
<p>Before we get into how you can query data, let’s take a quick recap of AWS DynamoDB.</p>
<p>DynamoDB is a cloud-hosted NoSQL database provided by Amazon Web Services (AWS). DynamoDB provides <em>reliable performance,</em> a <em>managed experience,</em> and convenient <em>API access</em> to interact with it.</p>
<p>If you are new to DynamoDB, I highly recommend checking out my AWS DynamoDB For the .NET Developer article below to understand better.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.rahulpnath.com/blog/aws-dynamodb-net-core/">https://www.rahulpnath.com/blog/aws-dynamodb-net-core/</a></div>
<p> </p>
<blockquote>
<p>One of the reasons why it can be a bit tricky with querying data is that you almost always need the partition key to get any data out of DynamoDB.</p>
</blockquote>
<p>While there are ways to get data without it, which we will see towards the end of this article, it’s generally not recommended and not performant.</p>
<p>Let’s learn the different ways to filter and select data from DynamoDB using .NET.</p>
<h2 id="heading-net-and-dynamodb-setup">.NET And DynamoDB Setup</h2>
<p>The sample application I am using is the default ASP NET Core Web API Template that Visual Studio creates, with the <code>WeatherForecastController</code>.</p>
<p>I have updated the API to add a <code>CityName</code> property to the <code>WeatherForecast</code> class. I have also set up Dependency Injection to inject the <code>IDynamoDBContext</code> context into the Controller.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> dynamoDbClient = <span class="hljs-keyword">new</span> AmazonDynamoDBClient(
    FallbackCredentialsFactory.GetCredentials(), RegionEndpoint.APSoutheast2);
builder.Services.AddSingleton&lt;IAmazonDynamoDB&gt;(dynamoDbClient);
builder.Services.AddSingleton&lt;IDynamoDBContext, DynamoDBContext&gt;();
</code></pre>
<p>If any of this is unclear, check the previous blog post on <a target="_blank" href="https://www.rahulpnath.com/blog/aws-dynamodb-net-core/">Getting Started With DynamoDB</a>.</p>
<h2 id="heading-1-load-specific-item">1. Load Specific Item</h2>
<p>To load a specific item from the DynamoDB, we require both the Hash key and the Range Key, in this case, the <code>CityName</code> and <code>DateTime</code>.</p>
<p>The <code>LoadAsync</code> method, takes in both these as parameters and returns the <code>WeatherForecast</code> object.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">HttpGet(<span class="hljs-meta-string">"specific-date"</span>)</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;WeatherForecast&gt; <span class="hljs-title">GetAsync</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> cityName, DateTime date</span>)</span>
{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> _dynamoDbContext.LoadAsync&lt;WeatherForecast&gt;(cityName, date);
}
</code></pre>
<p>If an item with that Hash and Range Key does not exist, the method returns null. So make sure your application handles the scenario when the item does not exist.</p>
<p>For an API, you can return a <code>404 NotFound</code>.</p>
<h2 id="heading-2-query-by-partition-key">2. Query By Partition Key</h2>
<p>Often applications and users need to get more data than just one specific item.</p>
<p>This is most commonly required when having a List/Table shown in the application UI.</p>
<p>Let's say we need an API endpoint to return all the <code>WeatherData</code> for a given City.</p>
<p>In this case, all we have is the <code>CityName</code>, which is the Hash key.</p>
<p>We can use the <code>QueryAsync</code> method and pass in the hash key to it, as shown below.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">HttpGet(<span class="hljs-meta-string">"city-all"</span>)</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;IEnumerable&lt;WeatherForecast&gt;&gt;  GetAsync(<span class="hljs-keyword">string</span> cityName)
{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> _dynamoDbContext.QueryAsync&lt;WeatherForecast&gt;(cityName).GetRemainingAsync();
}
</code></pre>
<p>The <code>QueryAsync</code> method returns an <code>AsyncSearch&lt;T&gt;</code> as the response, which has additional methods to retrieve data from DynamoDb.</p>
<p>In the sample above, I use the <code>GetRemainingAsync</code> method to fetch all the items.</p>
<p><strong>Note:</strong> The <code>GetRemainingAsync</code> method on <code>AsyncSearch</code>, loops through and fetches all the items matching the specified condition. This could result in more than one request to DynamoDB.</p>
<h2 id="heading-3-query-by-partition-and-sort-key">3. Query By Partition and Sort Key</h2>
<p>When using the <code>QueryAsync</code> method, you can further filter the items that get returned. For a given city, you might have weather data from a long time.</p>
<p>And the application might be interested only in the last month's data or three, for example.</p>
<p>In these scenarios, we can use the <code>QueryOperator</code> and pass in the range key along with it.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">HttpGet(<span class="hljs-meta-string">"city-date-filter"</span>)</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;IEnumerable&lt;WeatherForecast&gt;&gt; GetAsync(<span class="hljs-keyword">string</span> cityName, DateTime dateTime)
{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> _dynamoDbContext.QueryAsync&lt;WeatherForecast&gt;(cityName, QueryOperator.GreaterThan,
            <span class="hljs-keyword">new</span> <span class="hljs-keyword">object</span>[] {dateTime})
        .GetRemainingAsync();
}
</code></pre>
<p>Above the <code>QueryOperator.GreaterThan</code>, returns all the Weather Data items that are about the specified DateTime.</p>
<p>Since the greater than operator requires only one parameter, we need to pass in only one DateTime.</p>
<p>However, if you are using the <code>QueryOperator.Between</code>, which requires two dates, you need to pass them in order (start and end) to the object array, as shown below.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">HttpGet(<span class="hljs-meta-string">"city-date-filter"</span>)</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;IEnumerable&lt;WeatherForecast&gt;&gt; GetAsync(
    <span class="hljs-keyword">string</span> cityName, DateTime dateTime, DateTime? endDateTime)
{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> _dynamoDbContext
        .QueryAsync&lt;WeatherForecast&gt;(
           cityName,
           QueryOperator.Between,
           <span class="hljs-keyword">new</span> <span class="hljs-keyword">object</span>[] { dateTime, endDateTime })
        .GetRemainingAsync();
}
</code></pre>
<h2 id="heading-4-using-low-level-net-sdk-api-for-queries">4. Using Low-level .NET SDK API For Queries</h2>
<p>Now that we understand how to filter and select data based on the Hash and range keys let’s see how we can further filter the data based on other properties on our DynamoDB Items.</p>
<p>Let’s say we need to get only Weather data for a city since last month where the temperature was above 25°C.</p>
<p>The High level <code>DynamoDBContext</code> API in the .NET SDK does not support this level of data filtering. For this, we need to switch down to the Low-Level APIs in the DynamoDB .NET SDK.</p>
<p>The <code>IAmazonDynamoDB</code> can be used for advanced data filtering scenarios like this.</p>
<p>In <code>Program.cs</code> we have already set up the <code>AmazonDynamoDBClient</code>which implements <code>IAmazonDynamoDB</code>, to be dependency injected.</p>
<p>The <code>IAmazonDynamoDB</code> has a similar <code>QueryAsync</code> method, which takes in a more complex <code>QueryRequest</code> class.</p>
<p>Let’s first understand the <code>QueryRequest</code> class and its usage by using it in a simple use case to get all the data for a given city Name (Hash key) before we go into the more advanced scenarios.</p>
<h3 id="heading-query-with-hash-using-keyconditions">Query with Hash using KeyConditions</h3>
<p>The <code>QueryRequest</code> expects us to pass the <code>TableName</code> and the <code>KeyConditions</code>. The <code>KeyConditions</code> takes in a dictionary of property names and the associated conditions for those properties.</p>
<p>In our case, since we want to get all the weather forecast items for the city name, let’s pass in the <code>CityName</code> property and the Condition <code>ComparisonOperator.EQ</code> to the expected city name, as shown below.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">HttpGet(<span class="hljs-meta-string">"city-all-low-level"</span>)</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;IEnumerable&lt;WeatherForecast&gt;&gt; GetLowLevelAsync(<span class="hljs-keyword">string</span> cityName)
{
    <span class="hljs-keyword">var</span> request = <span class="hljs-keyword">new</span> QueryRequest()
    {
        TableName = <span class="hljs-keyword">nameof</span>(WeatherForecast),
        KeyConditions = <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, Condition&gt;()
        {
            {
                <span class="hljs-keyword">nameof</span>(WeatherForecast.CityName),
                <span class="hljs-keyword">new</span> Condition()
                {
                    ComparisonOperator = ComparisonOperator.EQ,
                    AttributeValueList = <span class="hljs-keyword">new</span> List&lt;AttributeValue&gt;() {<span class="hljs-keyword">new</span>(cityName)}
                }
            }
        }
    };

    <span class="hljs-keyword">var</span> response = <span class="hljs-keyword">await</span> _amazonDynamoDbClient.QueryAsync(request);
    <span class="hljs-keyword">return</span> response.Items
        .Select(Document.FromAttributeMap)
        .Select(_dynamoDbContext.FromDocument&lt;WeatherForecast&gt;);
}
</code></pre>
<p>The <code>QueryAsync</code> being part of the Low-Level APIs does not return our custom-defined .NET class types (<code>WeatherForecast</code> in this case). It returns the Items as a <code>List&lt;Dictionary&lt;string, AttributeValue&gt;&gt;</code> type.</p>
<p>To convert it into a strongly typed .NET class, use the <code>Document.FromAttributeMap</code> helper method and then use the <code>FromDocument</code> method on the <code>DynamoDBContext</code> as shown above.</p>
<p>With the above code, we are now successfully using the .NET Low-Level APIs to query the DynamoDB directly.</p>
<h3 id="heading-query-with-hash-using-keyconditionexpression">Query with Hash using KeyConditionExpression</h3>
<p>Using the <code>KeyConditions</code> adds in a lot more boilerplate code, which can be avoided by using the <code>KeyConditionExpression</code> property.</p>
<p>With this new property, you only need to specify the equality comparison condition as a plain string with a placeholder name for the actual value. This is similar to using a SQL query and passing parameters to it.</p>
<p>Below we have the <code>KeyConditionExpression</code> to get all <code>CityName</code> matching to the placeholder <em>‘:cityName’.</em> To pass in the actual value for the city name, we use the <code>ExpressionAttributeValues</code> property.</p>
<p>Below we need a key-value pair for the cityName placeholder and its associated value, which is the actual city name we are looking to get the data for.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> request = <span class="hljs-keyword">new</span> QueryRequest()
{
    TableName = <span class="hljs-keyword">nameof</span>(WeatherForecast),
    KeyConditionExpression = <span class="hljs-string">"CityName = :cityName"</span>,
    ExpressionAttributeValues = <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, AttributeValue&gt;()
    {
        {<span class="hljs-string">":cityName"</span>, <span class="hljs-keyword">new</span> AttributeValue(cityName)}
    }
};
</code></pre>
<p>This is way less boilerplate code and much more readable.</p>
<p><strong>Reserved Keywords in KeyConditionExpression and Attribute Names</strong></p>
<p>If we want to further filter the data by the date as we did in the earlier examples, we can do that using the same <code>KeyConditionExpression</code>. All we need to do is append the condition on the Sort Key using the ‘and’ keyword and pass in the additional <code>ExpressionAttributeValues</code>.</p>
<p>For example: To filter the data by items after a given date, we can use the below expression.</p>
<pre><code class="lang-csharp">    KeyConditionExpression = <span class="hljs-string">"CityName = :cityName and Date &gt; :startDate"</span>,
</code></pre>
<p>However, in this particular case, this query will fail since the property name ‘Date’ conflicts with a <a target="_blank" href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html">DynamoDB Reserved Keyword</a>.</p>
<p>In these scenarios, where the property names conflict with DynamoDB Reserved Keywords, you need to pass the property name also as placeholder properties and send the corresponding values using the <code>ExpressionAttributeNames</code> property.</p>
<p>As shown below, the <code>KeyConditionExpression</code> now specifies a placeholder ‘#Date’ for the Date property and adds the appropriate mapping for the placeholder to the actual property name in the <code>ExpressionAttributeNames</code> property.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> request = <span class="hljs-keyword">new</span> QueryRequest()
{
    TableName = <span class="hljs-keyword">nameof</span>(WeatherForecast),
    KeyConditionExpression = <span class="hljs-string">"CityName = :cityName and #Date &gt; :startDate"</span>,
    ExpressionAttributeNames = <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>&gt;()
    {
        {<span class="hljs-string">"#Date"</span>, <span class="hljs-string">"Date"</span>}
    },
    ExpressionAttributeValues = <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, AttributeValue&gt;()
    {
        {<span class="hljs-string">":cityName"</span>, <span class="hljs-keyword">new</span> AttributeValue(cityName)},
        {<span class="hljs-string">":startDate"</span>, <span class="hljs-keyword">new</span> AttributeValue(startDate.ToString(AWSSDKUtils.ISO8601DateFormat))}
    }
};
</code></pre>
<p>This helps us to query the data using the Low-Level .NET DynamoDB APIs and filter data based on both the Hash and sort keys.</p>
<h2 id="heading-5-filtering-data-by-non-key-properties">5. Filtering Data By Non-Key Properties</h2>
<p>Finally, let’s filter the data based on the <code>Temperature</code> property, which is why we set down the path of exploring the Low-Level APIs.</p>
<p>Using the <code>FilterExpression</code> property, we can further filter down the data filtered by the <code>KeyConditionExpression</code>.</p>
<p>Since in this case, we need to get all the weather data items with a temperature greater than a specific value, let’s add that condition to <code>FIlterExpression</code> property as shown below.</p>
<p>This again uses the same placeholder name for the value, and it passes the actual value as part of the <code>ExpressionAttributeValues</code> as shown below.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> request = <span class="hljs-keyword">new</span> QueryRequest()
{
    TableName = <span class="hljs-keyword">nameof</span>(WeatherForecast),
    KeyConditionExpression = <span class="hljs-string">"CityName = :cityName and #Date &gt; :startDate"</span>,
    FilterExpression = <span class="hljs-string">"TemperatureC &gt;= :minTemp"</span>,
    ExpressionAttributeNames = <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>&gt;()
    {
        {<span class="hljs-string">"#Date"</span>, <span class="hljs-string">"Date"</span>}
    },
    ExpressionAttributeValues = <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, AttributeValue&gt;()
    {
        {<span class="hljs-string">":cityName"</span>, <span class="hljs-keyword">new</span> AttributeValue(cityName)},
        {<span class="hljs-string">":startDate"</span>, <span class="hljs-keyword">new</span> AttributeValue(startDate.ToString(AWSSDKUtils.ISO8601DateFormat))},
        {<span class="hljs-string">":minTemp"</span>, <span class="hljs-keyword">new</span> AttributeValue() {N = minTemp.ToString()}}
    },
};
</code></pre>
<p>This filters the records that are greater than or equal to the minimum temperature passed in.</p>
<p><strong>Filter Expression Using Between</strong></p>
<p>Both the <code>KeyConditionExpression</code> and the <code>FilterExpression</code> supports using the between operator as well.</p>
<p>All we need to do is pass in multiple parameters for the between operator using the and operator.</p>
<p>Below is an example that uses the between operator in <code>FilterExpression</code> to filter all the items within a given temperature range.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> request = <span class="hljs-keyword">new</span> QueryRequest()
{
    TableName = <span class="hljs-keyword">nameof</span>(WeatherForecast),
    KeyConditionExpression = <span class="hljs-string">"CityName = :cityName and #Date &gt; :startDate"</span>,
    FilterExpression = <span class="hljs-string">"TemperatureC between :minTemp and :maxTemp"</span>,
    ExpressionAttributeNames = <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>&gt;()
    {
        {<span class="hljs-string">"#Date"</span>, <span class="hljs-string">"Date"</span>}
    },
    ExpressionAttributeValues = <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, AttributeValue&gt;()
    {
        {<span class="hljs-string">":cityName"</span>, <span class="hljs-keyword">new</span> AttributeValue(cityName)},
        {<span class="hljs-string">":startDate"</span>, <span class="hljs-keyword">new</span> AttributeValue(startDate.ToString(AWSSDKUtils.ISO8601DateFormat))},
        {<span class="hljs-string">":minTemp"</span>, <span class="hljs-keyword">new</span> AttributeValue() {N = minTemp.ToString()}},
        {<span class="hljs-string">":maxTemp"</span>, <span class="hljs-keyword">new</span> AttributeValue() {N = maxTemp.ToString()}}
    },
};
</code></pre>
<h2 id="heading-6-scan-the-whole-table-avoid-using-this">6. Scan The Whole Table (Avoid Using This)</h2>
<p>A Scan Operation does exactly as its name; it scans the entire table, looking for each item that matches the specified criteria.</p>
<p>I’ve kept this to the last because I was hoping you could try and avoid using this as much as possible.</p>
<p>Scans can also be costlier for the same reason. Since you are billed based on the data scanned and not on the date returned.</p>
<p>Below is an example of using Scan to find items where the temperature is greater than 30 degrees Celsius.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> scanItems = <span class="hljs-keyword">await</span> _dynamoDbContext.ScanAsync&lt;WeatherForecast&gt;(
    <span class="hljs-keyword">new</span>[]
    {
        <span class="hljs-keyword">new</span> ScanCondition(<span class="hljs-keyword">nameof</span>(WeatherForecast.TemperatureC), ScanOperator.GreaterThan, <span class="hljs-number">30</span>)
    }).GetRemainingAsync();
</code></pre>
<p>When running this, it loops through all the items in the DynamoDB and checks if the <code>TemperatureC</code> property is greater than the given value. If you have a database with a large number of items, this is going to be a slow and costly operation.</p>
<p>I hope you now can query data from DynamoDB without having to scan through it. If not, we will see how you could use Indexes to solve and make your use case faster in a future article.</p>
]]></content:encoded></item><item><title><![CDATA[Add to Apple Wallet from Your .NET Application: A Step-by-Step Guide]]></title><description><![CDATA[I recently worked on the 'Add to Apple Wallet' functionality at work, allowing the addition of Event tickets to Apple Wallet.
The iOS Wallet app allows users to manage payment cards, boarding passes, tickets, gift cards, and other passes.
The Wallet ...]]></description><link>https://rahulpnath.hashnode.dev/add-to-apple-wallet-from-your-net-application-a-step-by-step-guide</link><guid isPermaLink="true">https://rahulpnath.hashnode.dev/add-to-apple-wallet-from-your-net-application-a-step-by-step-guide</guid><category><![CDATA[dotnet]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Apple]]></category><dc:creator><![CDATA[Rahul Nath]]></dc:creator><pubDate>Thu, 08 Feb 2024 04:43:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1707367263634/5f9bf18f-00e6-4aa4-aef5-29599a3738e9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I recently worked on the 'Add to Apple Wallet' functionality at work, allowing the addition of Event tickets to Apple Wallet.</p>
<p>The iOS Wallet app allows users to manage payment cards, boarding passes, tickets, gift cards, and other passes.</p>
<p>The Wallet Pass is time and location enabled, so passes can be configured to display on the user’s device at the appropriate moment. Passes can also be updated with push notifications making it easy to notify users if details change.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/mcR08yyFpaU">https://youtu.be/mcR08yyFpaU</a></div>
<p> </p>
<p>In this blog post, let's learn how you can set up, build, and distribute Apple Wallet passes using .NET application.</p>
<p>I will use an AWS Lambda Function to host the API endpoint for distributing the pass files. However, you can use your existing application hosting mechanism for this.</p>
<h2 id="heading-apple-wallet-passes">Apple Wallet Passes</h2>
<p><a target="_blank" href="https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/PassKit_PG/Creating.html#//apple_ref/doc/uid/TP40012195-CH4-SW1">Apple Wallet Passes</a> are created as a package/bundle, containing a <code>pass.json</code> file that defines the pass, and image assets such as the logo and the icon.</p>
<p>The <code>pass.json</code> file contains the information that identifies the pass, the text information, and details shown on the pass in the Wallet app.</p>
<p>The below image shows the files inside a sample pass file. It has the <code>pass.json</code>, logo and icon images, the manifest file, and also the signature file to avoid tampering with the pass file once it's generated.</p>
<p><img src="https://www.rahulpnath.com/content/images/2023/09/image.png" alt /></p>
<p>Imaging showing the files inside a pass file. It contains the pass.json file, along with the icon and logo images, the manifest file and the signature file for the pass contents.</p>
<h2 id="heading-generating-wallet-passes-using-net">Generating Wallet Passes Using .NET</h2>
<p>To generate Apple Wallet Pass files from .NET applications, you can use the <a target="_blank" href="https://github.com/tomasmcguinness/dotnet-passbook">dotnet-passbook</a> NuGet package.</p>
<p>The NuGet package makes it very easy and straightforward to create Wallet Pass files. The NuGet package provides a <code>PassGenerator</code> class that takes in one or more instances of <code>PassGeneratorRequest</code>.</p>
<p>The <code>PassGenerator</code> converts these requests into a byte array, which represents the pass bundle that contains one or more pass files.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;APIGatewayHttpApiV2ProxyResponse&gt; <span class="hljs-title">GetAppleWalletPasses</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-keyword">var</span> eventId = Guid.NewGuid();
    <span class="hljs-keyword">var</span> eventName = <span class="hljs-string">"YouTube Demo Event"</span>;
    <span class="hljs-keyword">var</span> venueName = <span class="hljs-string">"YouTube Online"</span>;
    <span class="hljs-keyword">var</span> eventDate = DateTime.Now.AddDays(<span class="hljs-number">55</span>);

    <span class="hljs-keyword">var</span> icon = <span class="hljs-keyword">await</span> _appleWalletConfiguration.GetIcon();
    <span class="hljs-keyword">var</span> logo = <span class="hljs-keyword">await</span> _appleWalletConfiguration.GetLogo();

    <span class="hljs-keyword">var</span> request = GeneratePassRequest(Guid.NewGuid().ToString(), eventId, icon, logo, eventName, eventDate, venueName, <span class="hljs-string">"Subscriber"</span>);

    <span class="hljs-keyword">var</span> generator = <span class="hljs-keyword">new</span> PassGenerator();
    <span class="hljs-keyword">var</span> requests = <span class="hljs-keyword">new</span> List&lt;PassGeneratorRequest&gt;() { request };
    <span class="hljs-keyword">var</span> pass = generator.Generate(requests);
}
</code></pre>
<p>The <code>PassGeneratorRequest</code> identifies one pass that will be added to the Apple Wallet. If you have multiple passes to be added in the same bundle, create multiple <code>PassGeneratorRequest</code>.</p>
<p>The below code shows a sample <code>PassGeneratorRequest</code> that adds a pass with a <code>EventTicket</code> style. It sets the relevant properties to be shown on the Apple Wallet Pass.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> request = <span class="hljs-keyword">new</span> PassGeneratorRequest
{
    Style = PassStyle.EventTicket,
    PassTypeIdentifier = _appleWalletConfiguration.PassTypeIdentifier,
    SerialNumber = serialNumber,
    GroupingIdentifier = eventId.ToString(),
    BackgroundColor = <span class="hljs-string">"#823EB7"</span>,
    LabelColor = <span class="hljs-string">"#000000"</span>,
    ForegroundColor = <span class="hljs-string">"#ffffff"</span>,
    Images =
    {
        {PassbookImage.Icon, icon},
        {PassbookImage.Icon2X, icon},
        {PassbookImage.Icon3X, icon},
        {PassbookImage.Logo, logo},
        {PassbookImage.Logo2X, logo},
        {PassbookImage.Logo3X, logo},
    },
    Description = eventName,
    OrganizationName = <span class="hljs-string">"Rahul"</span>,
    RelevantDate = eventDate,
    ExpirationDate = eventDate.AddDays(<span class="hljs-number">1</span>),
    AppleWWDRCACertificate = _appleWalletConfiguration.AppleWWDRCACertificate(),
    PassbookCertificate = _appleWalletConfiguration.PassbookCertificate()
};
request.AddHeaderField(<span class="hljs-keyword">new</span> StandardField(<span class="hljs-string">"time"</span>, eventDate.ToShortTimeString(),
    eventDate.ToShortDateString()));
request.AddPrimaryField(<span class="hljs-keyword">new</span> StandardField(<span class="hljs-string">"name"</span>, <span class="hljs-literal">null</span>, eventName));
request.AddSecondaryField(<span class="hljs-keyword">new</span> StandardField(<span class="hljs-string">"venue"</span>, <span class="hljs-string">"Venue"</span>, venueName));
request.AddAuxiliaryField(<span class="hljs-keyword">new</span> StandardField(<span class="hljs-string">"ticketType"</span>, <span class="hljs-string">"Ticket Type"</span>, ticketType));
request.AddBackField(<span class="hljs-keyword">new</span> StandardField(<span class="hljs-string">"ticketHolderName-back"</span>, <span class="hljs-string">"Ticket holder"</span>, <span class="hljs-string">"Rahul Nath"</span>));
request.AddBackField(<span class="hljs-keyword">new</span> StandardField(<span class="hljs-string">"ticketType-back"</span>, <span class="hljs-string">"Ticket Type"</span>, ticketType));
</code></pre>
<p>For information on the different fields, the pass styles available, and what each property represents check out the <a target="_blank" href="https://developer.apple.com/design/human-interface-guidelines/wallet">Wallet Guidelines</a> and the <a target="_blank" href="https://developer.apple.com/documentation/walletpasses/pass">documentation</a>.</p>
<h3 id="heading-setting-up-apple-certificates">Setting Up Apple Certificates</h3>
<p>To generate Apple Wallet Passes, you need to register with the Apple Developer Portal and have the appropriate certificates ready to sign the Wallet passes.</p>
<p>You need two certificates</p>
<ul>
<li><p>Your application/company Passbook certificate along with its password</p>
</li>
<li><p>Apple WWDR (WorldWide Developer Relations) certificate</p>
</li>
</ul>
<p>You can find the full instructions to generate the certificates in the dotnet-passbook NuGet package <a target="_blank" href="https://github.com/tomasmcguinness/dotnet-passbook#certificates">documentation here</a>.</p>
<p>You can store these certificates and other related configurations for generating the Wallet as part of the application configuration - <em>appsettings.json</em> file<em>.</em></p>
<p>I chose to store the certificate files as base64 encoded string which can be converted to a <code>X509Certificate2</code> in .NET code as shown below.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">AppleWalletConfiguration</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> WWDRCertificateBase64 { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> PassTypeIdentifier { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> PassbookCertificateBase64 { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> PassbookPassword { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> IconUrl { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> LogoUrl { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

    <span class="hljs-function"><span class="hljs-keyword">public</span> X509Certificate2 <span class="hljs-title">AppleWWDRCACertificate</span>(<span class="hljs-params"></span>)</span> =&gt;
        <span class="hljs-keyword">new</span>(Convert.FromBase64String(WWDRCertificateBase64));

    <span class="hljs-function"><span class="hljs-keyword">public</span> X509Certificate2 <span class="hljs-title">PassbookCertificate</span>(<span class="hljs-params"></span>)</span> =&gt;
        <span class="hljs-keyword">new</span>(Convert.FromBase64String(PassbookCertificateBase64), PassbookPassword);

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;<span class="hljs-keyword">byte</span>[]&gt; GetLogo() =&gt; <span class="hljs-keyword">await</span> LogoUrl.GetBytesAsync();

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;<span class="hljs-keyword">byte</span>[]&gt; GetIcon() =&gt; <span class="hljs-keyword">await</span> IconUrl.GetBytesAsync();
}
</code></pre>
<h2 id="heading-distributing-wallet-pass-files-using-net">Distributing Wallet Pass Files Using .NET</h2>
<p>To distribute the Wallet Pass Files generated, you can host this in an API endpoint and share the link via email, a button on a web page, or an app.</p>
<p>You can host this along with your other application code on the Cloud or your own servers.</p>
<p>For this post, I'll host this AWS Lambda, as it's a quick and easy way to host such kind of API endpoints. Using the latest Lambda Annotations Framework makes it even easier to create and host API endpoints.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.rahulpnath.com/blog/lambda-annotation-framework-crud-api/">https://www.rahulpnath.com/blog/lambda-annotation-framework-crud-api/</a></div>
<p> </p>
<p>All we need to do is add the <a target="_blank" href="https://www.rahulpnath.com/blog/aws-lambda-annotation-framework/">Lambda Annotation Framework</a> NuGet package and wrap our Pass Generation function with the <code>LambdaFunction</code> attribute. Since this is going to be exposed as an API will also add the <code>HttpApi</code> attribute and specify the route and the method <code>Get</code>.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">LambdaFunction(Policies = <span class="hljs-meta-string">"AWSLambdaBasicExecutionRole"</span>)</span>]
[<span class="hljs-meta">HttpApi(LambdaHttpMethod.Get, <span class="hljs-meta-string">"/apple-wallet"</span>)</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;APIGatewayHttpApiV2ProxyResponse&gt; <span class="hljs-title">GetAppleWalletPasses</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-keyword">var</span> eventId = Guid.NewGuid();
    <span class="hljs-keyword">var</span> eventName = <span class="hljs-string">"YouTube Demo Event"</span>;
    ...
    <span class="hljs-keyword">var</span> generator = <span class="hljs-keyword">new</span> PassGenerator();
    <span class="hljs-keyword">var</span> requests = <span class="hljs-keyword">new</span> List&lt;PassGeneratorRequest&gt;() { request };
    <span class="hljs-keyword">var</span> pass = generator.Generate(requests);

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> APIGatewayHttpApiV2ProxyResponse()
    {
        Body = Convert.ToBase64String(pass),
        IsBase64Encoded = <span class="hljs-literal">true</span>,
        StatusCode = <span class="hljs-number">200</span>,
        Headers = <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>&gt;
        {
            { <span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"application/vnd.apple.pkpasses"</span> },
            { <span class="hljs-string">"Content-Disposition"</span>, <span class="hljs-string">"attachment; filename=tickets.pkpasses.zip; filename*=UTF-8''tickets.pkpasses.zip"</span> }
        }
    };
}
</code></pre>
<p>The <code>HttpApi</code> attribute allows us to expose the Lambda Function over a <a target="_blank" href="https://www.rahulpnath.com/blog/function-urls-in-aws-lambda-dotnet/">Lambda Function URL</a> or an <a target="_blank" href="https://www.rahulpnath.com/blog/amazon-api-gateway-http-apis/">HTTP API Gateway endpoint</a>.</p>
<p>Both of these mechanisms expect binary content to be encoded as base64 string, with the appropriate HTTP headers set. To achieve this we can use the <code>APIGatewayHttpApiV2ProxyResponse</code> object as shown above.</p>
<p>Once deployed, you can navigate the API Gateway URL endpoint or expose a Lambda Function URL, and navigate to the URL from an iPhone Safari browser, to add it to Apple Wallet.</p>
<p>Below is the screenshot of the pass generated using the above code, with the appropriate information we passed to create the pass.</p>
<p><img src="https://www.rahulpnath.com/content/images/2023/10/image-1.png" alt /></p>
<p>Apple Wallet Passes added to phone showing the front and the back details of the pass.</p>
<p>For debugging pass files you can use the <a target="_blank" href="https://pkpassvalidator.azurewebsites.net/">PKPassValidator service</a>, where you can upload a pass file and it will show any issues with the file generated.</p>
<p>The Apple Wallet pass generated above is static and does not update itself.</p>
<p>However most of the time you would need to deliver live updates to these passes in case any information changes for example date changes, venue changes, status updates, etc. For this, we can configure the <code>WebServiceUrl</code>, on the pass that it will poll for pass updates</p>
<p>We will learn how to configure and set up the CallbackUrl in a future blog post. Until then, happy coding! 👋</p>
]]></content:encoded></item><item><title><![CDATA[How I Wake Up at 4 AM Every Day!]]></title><description><![CDATA[I have always been the 'early to bed' type of person, and my day mostly ends by nine-thirty in the evening.
But the 'waking up early' part was not as 'strict' as the going to sleep part.
I have always struggled to wake up at the alarm sound, which I ...]]></description><link>https://rahulpnath.hashnode.dev/how-i-wake-up-at-4-am-every-day</link><guid isPermaLink="true">https://rahulpnath.hashnode.dev/how-i-wake-up-at-4-am-every-day</guid><category><![CDATA[Productivity]]></category><category><![CDATA[tips]]></category><dc:creator><![CDATA[Rahul Nath]]></dc:creator><pubDate>Wed, 07 Feb 2024 06:11:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1707286057525/ea186d8e-9974-4bca-a25c-8d0c09ea7408.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have always been the '<em>early to bed</em>' type of person, and my day mostly ends by nine-thirty in the evening.</p>
<p>But the '<em>waking up early</em>' part was not as 'strict' as the going to sleep part.</p>
<p>I have always struggled to wake up at the alarm sound, which I always set without fail. It was either me hitting the dismiss button or it going completely unheard, only to be reminded about it at breakfast by my wife.</p>
<p>I have noticed that it's not that waking up in the morning that is the problem. Waking up from bed at any time of the day is a problem, and we all tend to lie down for a few more minutes.</p>
<p>Below are some of the strategies that help me wake up at 4 am every day, including weekends.</p>
<p>Ok, most of the days, every day 😀</p>
<p><img src="https://www.rahulpnath.com/content/images/alarm.png" alt /></p>
<ol>
<li><p><strong>Plan:</strong> Plan your activities to be done after waking up, before going to sleep the day before. '<em>Not having anything to do</em>' is one of the main reasons that keeps us from waking up. So plan ahead for it and find activities like reading, writing, working out, or anything you like. But have a plan!  </p>
</li>
<li><p><strong>Jump out:</strong> Don't let your brain rationalize your plan at the sound of the alarm. The decision to wake up has already been made the night before, and you have a plan, so jump out of bed. The more you think, the more likely it is to doze off.</p>
</li>
<li><p><strong>Multiple alarms:</strong> At the early stages of trying this out, you can set multiple alarms at very close intervals, as close as one or two minutes. Have a couple at one, two, five, or ten-minute intervals. (If somebody else shares the room with you, make sure they don't mind this for a couple of days till you find the rhythm.)  </p>
</li>
<li><p><strong>Alarm tone:</strong> Change the alarm tone frequently so your brain does not get used to it and ignore it altogether. You could also try having the same tone as your phone ringer, making you think you have a call and wake up immediately.  </p>
</li>
<li><p><strong>Early dinner:</strong> Eat at least two to three hours before you sleep. Going to sleep with a full stomach makes you more tired in the morning and increases the chances of you having to hit the snooze button.  </p>
</li>
<li><p><strong>Daily alarms:</strong> Set your alarm daily and delete all the recurrent alarms for the entire week. This would ensure you plan for your day when setting the alarm. No plan, No alarm!  </p>
</li>
<li><p><strong>Sleep Cycle:</strong> Over time, listen to your body, understand how much sleep you require, and adjust your timings accordingly. Too little or too much sleep is bad.  </p>
</li>
<li><p><strong>Smart Alarms:</strong> There are mobile applications today that come up with puzzle based alarms, you would have to solve the puzzle to turn of the alarm. You could try one of those if needed(I haven't used any).</p>
</li>
</ol>
<p>These tips have helped me wake up to an alarm and be an early riser.</p>
<p>The feeling of starting the day as you wished for is great and stays throughout the day, helping you to do more and <a target="_blank" href="https://www.rahulpnath.com/blog/staying-organized-finding-a-system-to-manage-it-all/">stay organized</a>.</p>
<p>I hope you find this useful, and do let me know if any other tricks have helped you to wake up to an alarm.</p>
]]></content:encoded></item><item><title><![CDATA[How To Easily Build CRUD APIs In AWS Lambda Functions Using .NET]]></title><description><![CDATA[The Lambda Annotations Framework is a programming model that makes it easier to build AWS Lambda Functions using .NET.
The framework uses C# custom attributes and Source Generators to translate annotated Lambda functions to the regular Lambda program...]]></description><link>https://rahulpnath.hashnode.dev/how-to-easily-build-crud-apis-in-aws-lambda-functions-using-net</link><guid isPermaLink="true">https://rahulpnath.hashnode.dev/how-to-easily-build-crud-apis-in-aws-lambda-functions-using-net</guid><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[dotnet]]></category><dc:creator><![CDATA[Rahul Nath]]></dc:creator><pubDate>Tue, 06 Feb 2024 05:10:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1707196072926/6f02353b-acc7-4629-a74a-0cd48422da2a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The <a target="_blank" href="https://www.rahulpnath.com/blog/aws-lambda-annotation-framework/">Lambda Annotations Framework</a> is a programming model that makes it easier to build AWS Lambda Functions using .NET.</p>
<p>The framework uses C# custom attributes and Source Generators to translate annotated Lambda functions to the regular Lambda programming model.</p>
<p>In a <a target="_blank" href="https://www.rahulpnath.com/blog/aws-lambda-annotation-framework/">previous post</a>, we learned how to get started with using the Annotations Framework and how it compares to using the default Lambda Programming model. The Annotations Framework removes boilerplate code required to integrate with API Gateway.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=Zx6JpVKUoLQ">https://www.youtube.com/watch?v=Zx6JpVKUoLQ</a></div>
<p> </p>
<p>In this post, let’s learn how to set up a basic CRUD (Create, Read, Update, and Delete) Order API Endpoint using the Lambda Annotation Framework.</p>
<p>We will use DynamoDB as our data store for this example and we will also learn how to set up the DynamoDB Table and Role Permissions via the same CloudFormation template that the Annotations framework generates.</p>
<h2 id="heading-create-and-update-api-endpoint">Create and Update API Endpoint</h2>
<p>Let's first create the Create Endpoint for our API.</p>
<p>Using the Annotations Framework, we can use the <code>LambdaFunction</code> and <code>HttpApi</code> attribute to decorate a .NET function to be a Lambda Entry point.</p>
<p>The endpoints accept an object of type <code>Order</code> which represents an Order in our domain.</p>
<p>We have a simple <code>Order</code> class with the <code>OrderId</code> as it's primary key.</p>
<p>To bind the <code>Order</code> item from the requests body, we can use the <code>FromBody</code> attribute on the function parameter. This is very similar to how we do when building ASP.NET Core APIs.</p>
<p>⚠️</p>
<p>Make sure the template path does not have a leading slash on it.</p>
<p>We also have specified a custom IAM Role Name as part of the <code>Role</code> parameter in the <code>LambdaFunction</code> attribute. More on this later.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">LambdaFunction(Role = <span class="hljs-meta-string">"@OrdersApiLambdaExecutionRole"</span>)</span>]
[<span class="hljs-meta">HttpApi(LambdaHttpMethod.Post, template: <span class="hljs-meta-string">"/orders"</span>)</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">PostOrder</span>(<span class="hljs-params">[FromBody] Order order, ILambdaContext context</span>)</span>
{
    <span class="hljs-keyword">await</span> _dynamodbContext.SaveAsync(order);
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Order</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> OrderId { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">decimal</span> Total { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> DateTime CreatedDate { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<p>Using the <code>DynamoDBContext</code> we can save the <code>Order</code> into the table. If you are new to DynamoDB I highly recommend checking the below blog post to get started.</p>
<p><a target="_blank" href="https://www.rahulpnath.com/blog/amazon-dynamodb-dotnet-developer">Amazon DynamoDB For The .NET Developer</a></p>
<p><a target="_blank" href="https://www.rahulpnath.com/blog/amazon-dynamodb-dotnet-developer">This blog post is a collection of other posts that covers various aspects of Amazon DynamoDB and other services you can integrate with when building serverless applications.</a></p>
<p><img src="https://www.rahulpnath.com/content/images/size/w256h256/2022/10/logo-512x512.png" alt /></p>
<p><a target="_blank" href="https://www.rahulpnath.com/blog/amazon-dynamodb-dotnet-developer">Rahul NathRahul Pulikkot Nath</a></p>
<p><img src="https://www.rahulpnath.com/content/images/2023/06/Amazon-DynamoDB.png" alt /></p>
<p>For the DynamoDBContext save to work, we need to make sure the Table is set up and the Lambda Function has appropriate access to the table.</p>
<h2 id="heading-setting-up-dynamodb-table-and-iam-roles-via-cloudformation">Setting up DynamoDB Table and IAM Roles via CloudFormation</h2>
<p>As part of the Lambda Annotations Framework, it auto-generates a <code>serverless.template</code> CloudFormation template file.</p>
<p>However, you can manually add Resources to the template file.</p>
<p>In our case, we need to set up the DynamoDB Table and the Lambda Access Roles with the appropriate permission to talk to DynamoDB Table.</p>
<p>To set up the Order DynamoDB table, let's add the below to the Resources section of the template.</p>
<pre><code class="lang-json"><span class="hljs-string">"OrderTable"</span>: {
  <span class="hljs-attr">"Type"</span>: <span class="hljs-string">"AWS::DynamoDB::Table"</span>,
  <span class="hljs-attr">"Properties"</span>: {
    <span class="hljs-attr">"AttributeDefinitions"</span>: [
      {
        <span class="hljs-attr">"AttributeName"</span>: <span class="hljs-string">"OrderId"</span>,
        <span class="hljs-attr">"AttributeType"</span>: <span class="hljs-string">"S"</span>
      }
    ],
    <span class="hljs-attr">"KeySchema"</span>: [
      {
        <span class="hljs-attr">"AttributeName"</span>: <span class="hljs-string">"OrderId"</span>,
        <span class="hljs-attr">"KeyType"</span>: <span class="hljs-string">"HASH"</span>
      }
    ],
    <span class="hljs-attr">"TableName"</span>: <span class="hljs-string">"Order"</span>,
    <span class="hljs-attr">"BillingMode"</span>: <span class="hljs-string">"PAY_PER_REQUEST"</span>
  }
}
</code></pre>
<p>It sets up the DynamoDB table with a specific name and also the Keys for the table.</p>
<p>For setting up the Lambda IAM Role, let's use the below template in the Resources section.</p>
<p>It sets up full access to DynamoDB Order Table. Note I am using the CloudFormation function <code>Fn::GetAtt</code> to get the ARN of the DynamoDB Table created in the same template file.</p>
<pre><code class="lang-json"><span class="hljs-string">"OrdersApiLambdaExecutionRole"</span>: {
  <span class="hljs-attr">"Type"</span>: <span class="hljs-string">"AWS::IAM::Role"</span>,
  <span class="hljs-attr">"Properties"</span>: {
    <span class="hljs-attr">"AssumeRolePolicyDocument"</span>: {
      <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
      <span class="hljs-attr">"Statement"</span>: [
        {
          <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
          <span class="hljs-attr">"Principal"</span>: {
            <span class="hljs-attr">"Service"</span>: <span class="hljs-string">"lambda.amazonaws.com"</span>
          },
          <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"sts:AssumeRole"</span>
        }
      ]
    },
    <span class="hljs-attr">"ManagedPolicyArns"</span>: [
      <span class="hljs-string">"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"</span>
    ],
    <span class="hljs-attr">"Policies"</span>: [
      {
        <span class="hljs-attr">"PolicyName"</span>: <span class="hljs-string">"OrderApiDynamoDBAccessPolicy"</span>,
        <span class="hljs-attr">"PolicyDocument"</span>: {
          <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
          <span class="hljs-attr">"Statement"</span>: [
            {
              <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
              <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"dynamodb:*"</span>,
              <span class="hljs-attr">"Resource"</span>: {
                <span class="hljs-attr">"Fn::GetAtt"</span>: [<span class="hljs-string">"OrderTable"</span>, <span class="hljs-string">"Arn"</span>]
              }
            }
          ]
        }
      }
    ]
  }
}
</code></pre>
<p>The <code>LambdaFunction</code> attribute specifies the <code>Role</code> property to use the new IAM role that we just created above.</p>
<p>We use the name <code>Role = "@OrdersApiLambdaExecutionRole"</code> to refer to the IAM role created in the template file. The <code>@</code> prefix refers to look up for the name from the CloudFormation template.</p>
<p>This generates the below section to the CloudFormation template for the Lambda Function</p>
<pre><code class="lang-json"><span class="hljs-string">"LambdaAnnotationSampleOrderApiFunctionPostOrderGenerated"</span>: {
  <span class="hljs-attr">"Type"</span>: <span class="hljs-string">"AWS::Serverless::Function"</span>,
  <span class="hljs-attr">"Metadata"</span>: {
    <span class="hljs-attr">"Tool"</span>: <span class="hljs-string">"Amazon.Lambda.Annotations"</span>,
    <span class="hljs-attr">"SyncedEvents"</span>: [
      <span class="hljs-string">"RootPost"</span>
    ]
  },
  <span class="hljs-attr">"Properties"</span>: {
    <span class="hljs-attr">"Runtime"</span>: <span class="hljs-string">"dotnet6"</span>,
    <span class="hljs-attr">"CodeUri"</span>: <span class="hljs-string">"."</span>,
    <span class="hljs-attr">"MemorySize"</span>: <span class="hljs-number">256</span>,
    <span class="hljs-attr">"Timeout"</span>: <span class="hljs-number">30</span>,
    <span class="hljs-attr">"Role"</span>: {
      <span class="hljs-attr">"Fn::GetAtt"</span>: [
        <span class="hljs-string">"OrdersApiLambdaExecutionRole"</span>,
        <span class="hljs-string">"Arn"</span>
      ]
    },
    <span class="hljs-attr">"PackageType"</span>: <span class="hljs-string">"Zip"</span>,
    <span class="hljs-attr">"Handler"</span>: <span class="hljs-string">"LambdaAnnotationSample.OrderApi::LambdaAnnotationSample.OrderApi.Function_PostOrder_Generated::PostOrder"</span>,
    <span class="hljs-attr">"Events"</span>: {
      <span class="hljs-attr">"RootPost"</span>: {
        <span class="hljs-attr">"Type"</span>: <span class="hljs-string">"HttpApi"</span>,
        <span class="hljs-attr">"Properties"</span>: {
          <span class="hljs-attr">"Path"</span>: <span class="hljs-string">"/order"</span>,
          <span class="hljs-attr">"Method"</span>: <span class="hljs-string">"POST"</span>
        }
      }
    }
  }
}
</code></pre>
<p>You can deploy the template from the IDE or using the AWS CLI, which will create the Lambda Function, the IAM Role, the API Endpoint and wire up the integration for the route to the Lambda Function on the API Gateway.</p>
<h2 id="heading-read-api-endpoint">Read API Endpoint</h2>
<p>To set up the Read API Endpoint, all we need to do is create another Function in the same class and add the <code>LambdaFunction</code> and <code>HttpApi</code> attribute. Since this is a GET, it specifies the appropriate <code>LambdaHttpMethod</code> on it.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">LambdaFunction(Role = <span class="hljs-meta-string">"@OrdersApiLambdaExecutionRole"</span>)</span>]
[<span class="hljs-meta">HttpApi(LambdaHttpMethod.Get, <span class="hljs-meta-string">"/orders/{orderId}"</span>)</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;Order&gt; <span class="hljs-title">GetOrder</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> orderId, ILambdaContext context</span>)</span>
{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> _dynamodbContext.LoadAsync&lt;Order&gt;(orderId);
}
</code></pre>
<p>The function takes in the <code>orderId</code> as the parameter, which is bound from the path parameter in the request URL.</p>
<p>You can use the <code>DynamoDBContext</code> to retrieve the item from the Table and return the Order item.</p>
<p>Lambda Annotations will automatically convert this into JSON representation and send it as part of the HTTP response body.</p>
<h2 id="heading-delete-api-endpoint">Delete API Endpoint</h2>
<p>Very similar to the Get API Endpoint, we can add a new Function for the Delete endpoint.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">LambdaFunction(Role = <span class="hljs-meta-string">"@OrdersApiLambdaExecutionRole"</span>)</span>]
[<span class="hljs-meta">HttpApi(LambdaHttpMethod.Delete, <span class="hljs-meta-string">"/orders/{orderId}"</span>)</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">DeleteOrder</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> orderId, ILambdaContext context</span>)</span>
{
    <span class="hljs-keyword">await</span> _dynamodbContext.DeleteAsync&lt;Order&gt;(orderId);
}
</code></pre>
<p>It takes the <code>orderId</code> from the Path parameter and uses that to delete the item from the DynamoDB table.</p>
<h2 id="heading-annotations-framework-generated-code">Annotations Framework Generated Code</h2>
<p>The Lambda Annotations Framework uses .NET Source Generators to generate the boilerplate code to interact with the API Gateway.</p>
<p>Below is a screenshot of the DLL from <a target="_blank" href="https://www.jetbrains.com/decompiler/whatsnew/?ref=rahulpnath.com">JetBrains dotPeek application</a>, showing the generated code.</p>
<p><img src="https://www.rahulpnath.com/content/images/2023/08/image-1.png" alt /></p>
<p>Lambda Annotations Framework generated code using Source Generator.</p>
<p>As you can see, for each of the functions attributed with <code>LambdaFunction</code> it creates a new class, that wraps around the original function and adds the boilerplate code to talk with API Gateway.</p>
<p>This makes it easier for us developers to focus just on the business logic and not worry about how to integrate with the API Gateway.</p>
<p>You can find the full code sample <a target="_blank" href="https://github.com/rahulpnath/youtube-samples/tree/main/LambdaAnnotationSample/LambdaAnnotationSample.OrderApi">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Amazon MQ RabbitMQ: A Reliable Messaging Solution for Your .NET Projects]]></title><description><![CDATA[RabbitMQ is a powerful open-source message broker facilitating communication between systems or applications.
It ensures seamless data exchange by enabling asynchronous messaging, making it an essential tool for building scalable and resilient distri...]]></description><link>https://rahulpnath.hashnode.dev/amazon-mq-rabbitmq-a-reliable-messaging-solution-for-your-net-projects</link><guid isPermaLink="true">https://rahulpnath.hashnode.dev/amazon-mq-rabbitmq-a-reliable-messaging-solution-for-your-net-projects</guid><category><![CDATA[dotnet]]></category><category><![CDATA[rabbitmq]]></category><category><![CDATA[messaging]]></category><category><![CDATA[AWS]]></category><dc:creator><![CDATA[Rahul Nath]]></dc:creator><pubDate>Mon, 05 Feb 2024 07:11:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1707116867631/7934654c-f13c-4de1-9166-69beb10f1e52.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>RabbitMQ is a powerful open-source message broker facilitating communication between systems or applications.</p>
<p>It ensures seamless data exchange by enabling asynchronous messaging, making it an essential tool for building scalable and resilient distributed systems.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/4DDAGsXrNAQ?si=fBQx6nAblbMmnoJL">https://youtu.be/4DDAGsXrNAQ?si=fBQx6nAblbMmnoJL</a></div>
<p> </p>
<p>In this post, we will learn how to set up RabbitMQ and start using it from a .NET application to send and receive messages.</p>
<h2 id="heading-setting-up-rabbitmq">Setting Up RabbitMQ</h2>
<p>RabbitMQ can be hosted through self-hosted deployments, major cloud providers, containerization with Docker and Kubernetes, managed RabbitMQ providers, etc, offering a wide range of options from complete control to fully managed solutions.</p>
<p>For this post, I will use Amazon MQ, a managed message broker service that supports ActiveMQ and RabbitMQ engine types.</p>
<p>However, you can use <a target="_blank" href="https://www.rabbitmq.com/download.html">one of the various options that RabbitMQ provides</a> to host your instance, including a local Docker instance.</p>
<h3 id="heading-creating-rabbitmq-broker-on-amazon-mq">Creating RabbitMQ broker on Amazon MQ</h3>
<p><a target="_blank" href="https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/welcome.html">Amazon MQ</a> is a fully managed message broker service on AWS that supports popular messaging protocols, providing compatibility, reliability, scalability, and security for distributed applications.</p>
<p>Amazon MQ supports Apache ActiveMQ and RabbitMQ engine types.</p>
<p>To create a RabbitMQ message broker, navigate to Amazon MQ in the AWS Console.</p>
<p>Create a new broker and choose RabbitMQ as the broker engine type, as shown below.</p>
<p><img src="https://www.rahulpnath.com/content/images/2024/01/image-1.png" alt="Create a new Amazon MQ broker using the RabbitMQ broker type." /></p>
<p>Create a new Amazon MQ broker using the RabbitMQ broker type.</p>
<p>It then prompts you to choose the deployment mode.</p>
<ul>
<li><p><strong>Single-instance broker</strong> → one broker in one Availability Zone, mainly used for development and testing.</p>
</li>
<li><p><strong>Cluster deployment</strong> → logical grouping of three RabbitMQ broker nodes behind a Network Load Balancer, each sharing users, queues, and a distributed state across multiple Availability Zones</p>
</li>
</ul>
<p><img src="https://www.rahulpnath.com/content/images/2024/01/image-2.png" alt="Choose the deployment mode for Amazon MQ Rabbit broker instance - choose between single instance and cluster." /></p>
<p>Choose the deployment mode for Amazon MQ Rabbit broker instance - choose between single instance and cluster.</p>
<p>For purposes of this blog post, I am using a Single instance. However, choose Cluster deployment if you are setting up a production workload.</p>
<p>Once done you can configure the broker name, the instance type, username/password and other advanced configuration required.</p>
<p><img src="https://www.rahulpnath.com/content/images/2024/01/image-3.png" alt="Configure RabbitMQ setting in Amazon MQ." /></p>
<p>Configure RabbitMQ setting in Amazon MQ.</p>
<p>Review and create the RabbitMQ broker instance.</p>
<p><img src="https://www.rahulpnath.com/content/images/2024/01/image-5.png" alt="RabbitMQ broker instance up and running and the details as from the AWS console." /></p>
<p>RabbitMQ broker instance up and running and the details as from the AWS console.</p>
<h2 id="heading-creating-net-application-to-sendreceive-messages-from-rabbitmq">Creating .NET Application To Send/Receive Messages From RabbitMQ</h2>
<p>RabbitMQ is a message broker.</p>
<p>This means you would typically have a sender/producer and a receiver/consumer when building applications on it. The sender sends messages, and the receiver receives and processes them.</p>
<p>To start using the RabbitMQ broker instance set up above, let's first create two console applications - Send and Receive.</p>
<p>To connect and send/receive messages from RabbitMQ, we will use a NuGet package - <a target="_blank" href="https://www.nuget.org/packages/RabbitMQ.Client">RabbitMQ.Client</a>.</p>
<p>Below is a Powershell script to create the Send and Receive .NET console applications and add the required NuGet packages.</p>
<p>It creates a solution file, creates a Send console app with the start-up file as <code>Send.cs</code> , adds the NuGet package. It also creates a Receive console app with the start-up file <code>Receive.cs</code> and the required packages.</p>
<pre><code class="lang-powershell"><span class="hljs-comment"># Step 1: Create a new .NET solution</span>
dotnet new sln <span class="hljs-literal">-n</span> rabbitmq<span class="hljs-literal">-hello</span><span class="hljs-literal">-world</span>

<span class="hljs-comment"># Step 2: Create the 'Send' console application</span>
dotnet new console <span class="hljs-literal">-n</span> Send
dotnet add Send/Send.csproj package RabbitMQ.Client
dotnet sln rabbitmq<span class="hljs-literal">-hello</span><span class="hljs-literal">-world</span>.sln add Send/Send.csproj
<span class="hljs-built_in">mv</span> Send/Program.cs Send/Send.cs

<span class="hljs-comment"># Step 3: Create the 'Receive' console application</span>
dotnet new console <span class="hljs-literal">-n</span> Receive
dotnet add Receive/Receive.csproj package RabbitMQ.Client
dotnet sln rabbitmq<span class="hljs-literal">-hello</span><span class="hljs-literal">-world</span>.sln add Receive/Receive.csproj
<span class="hljs-built_in">mv</span> Receive/Program.cs Receive/Receive.cs
</code></pre>
<h2 id="heading-core-connection-concepts-with-rabbitmq">Core Connection Concepts With RabbitMQ</h2>
<p>The two core concepts that you need to know in terms of connecting with RabbitMQ are</p>
<ul>
<li><p><strong>Connection:</strong> This represents the TCP connection between your application and the RabbitMQ broker.</p>
</li>
<li><p><strong>Channels:</strong> A virtual connection within a connection<strong>.</strong> Used for specific tasks like publishing, consuming, and declaring queues and exchanges. Channels help organize and optimize communication between your application and the broker.</p>
</li>
</ul>
<p>Since it's undesirable to have multiple TCP connections open at the same time from our applications to RabbitMQ, we can create one Connection and then use Channels to open lightweight connections with the broker.</p>
<p>Channels in RabbitMQ support efficient multiplexing, isolation, resource management, concurrency, and performance optimization while still sharing the same underlying TCP connection.</p>
<h2 id="heading-connecting-net-application-with-rabbitmq">Connecting .NET Application with RabbitMQ</h2>
<p>To connect to RabbitMQ, let's use the <code>ConnectionFactory</code> from the <code>RabbitMQ.Client</code> NuGet package to create a new TCP Connection.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> factory = <span class="hljs-keyword">new</span> ConnectionFactory
{
    Uri = <span class="hljs-keyword">new</span> Uri(<span class="hljs-string">"amqps://b-71e1955a-1942-47e2-9e49-xxxxxxxxxxxx.mq.ap-southeast-2.amazonaws.com"</span>),
    Port = <span class="hljs-number">5671</span>,
    UserName = <span class="hljs-string">"&lt;USERNAME FROM CONFIGURATION FILE&gt;"</span>,
    Password = <span class="hljs-string">"&lt;PASSWORD FROM CONFIGURATION FILE&gt;"</span>
};
<span class="hljs-keyword">using</span> <span class="hljs-keyword">var</span> connection = factory.CreateConnection();
<span class="hljs-keyword">using</span> <span class="hljs-keyword">var</span> channel = connection.CreateModel();
</code></pre>
<p>The above code specifies the <code>Uri</code> to connect to the RabbitMQ instance in Amazon MQ and specify the username/password we used when configuring the RabbitMQ broker instance.</p>
<p>Using the Connection, it creates a Channel (referred to as Model in .NET speak), using the <code>CreateModel</code> function.</p>
<p>The Channel instance can then be used to send or receive messages from the RabbitMQ instance.</p>
<h3 id="heading-creating-a-queue-in-rabbitmq-from-net-application">Creating a Queue in RabbitMQ From .NET Application</h3>
<p>We first need to create a Queue to send or receive messages from RabbitMQ.</p>
<p>A Queue in RabbitMQ is a named message buffer that temporarily stores messages sent by producers and delivers them to consumers.</p>
<pre><code class="lang-csharp">channel.QueueDeclare(queue: <span class="hljs-string">"hello"</span>,
    durable: <span class="hljs-literal">false</span>,
    exclusive: <span class="hljs-literal">false</span>,
    autoDelete: <span class="hljs-literal">false</span>,
    arguments: <span class="hljs-literal">null</span>);
</code></pre>
<p>The above code uses the <code>QueueDeclare</code> function on the channel instance to create a simple queue.</p>
<p>Creating a Queue is an idempotent operation.</p>
<p>It means a new Queue is created only if it does not already exist. So it's safe to have this same code in both our Send/Receive applications since we cannot guarantee which one will run first, and we need the Queue to exist regardless of the order in which they are run.</p>
<p>We can now use this Queue to send/receive messages from our .NET application.</p>
<h3 id="heading-sending-messages-to-rabbitmq-from-net-application">Sending Messages to RabbitMQ From .NET Application</h3>
<p>To send a message to RabbitMQ, we can use the <code>BasicPublish</code> method on the channel instance.</p>
<p>Below we get the message text to send from the console and send it as the message body.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">string</span>? message = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">do</span>
{
    Console.WriteLine(<span class="hljs-string">"Enter Message. Press [enter] to exit."</span>);
    message = Console.ReadLine();
    <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">string</span>.IsNullOrEmpty(message))
        SendMessage(message, channel);
} <span class="hljs-keyword">while</span> (!<span class="hljs-keyword">string</span>.IsNullOrEmpty(message));

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">SendMessage</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> s, IModel channel</span>)</span>
{
    <span class="hljs-keyword">var</span> body = Encoding.UTF8.GetBytes(s);

    channel.BasicPublish(exchange: <span class="hljs-keyword">string</span>.Empty,
        routingKey: <span class="hljs-string">"hello"</span>,
        basicProperties: <span class="hljs-literal">null</span>,
        body: body);
    Console.WriteLine(<span class="hljs-string">$" [x] Sent <span class="hljs-subst">{s}</span>"</span>);
}
</code></pre>
<p>Note that we specify the <code>exchange</code> as empty and <code>routingKey</code> as the queue name. This uses the <a target="_blank" href="https://www.rabbitmq.com/tutorials/amqp-concepts.html#exchange-default">default exchange</a> that's available as part of RabbitMQ.</p>
<p>In a future blog post, we will learn more about Exchanges and the different types.</p>
<h3 id="heading-receive-messages-from-rabbitmq-from-net-application">Receive Messages From RabbitMQ From .NET Application</h3>
<p>With the message published successfully we are now ready to add in our first consumer/receiver for this message.</p>
<p>The <code>channel</code> instance provides a <code>BasicConsumer</code> method to register a consumer instance to a queue.</p>
<pre><code class="lang-csharp">Console.WriteLine(<span class="hljs-string">" [*] Waiting for messages."</span>);

<span class="hljs-keyword">var</span> consumer = <span class="hljs-keyword">new</span> EventingBasicConsumer(channel);
consumer.Received += (model, ea) =&gt;
{
    <span class="hljs-keyword">var</span> body = ea.Body.ToArray();
    <span class="hljs-keyword">var</span> message = Encoding.UTF8.GetString(body);
    Console.WriteLine(<span class="hljs-string">$" [x] Received <span class="hljs-subst">{message}</span>"</span>);
};
channel.BasicConsume(queue: <span class="hljs-string">"hello"</span>,
    autoAck: <span class="hljs-literal">true</span>,
    consumer: consumer);

Console.WriteLine(<span class="hljs-string">" Press [enter] to exit."</span>);
Console.ReadLine();
</code></pre>
<p>The RabbitMQ.Client NuGet package provides a <code>EventingBasicConsumer</code> class we can use to handle the message processing.</p>
<p>The above code sets up the <code>Received</code> event handler with the function to process the message, in this case, writing the message body to the console.</p>
<p><img src="https://www.rahulpnath.com/content/images/2024/01/rabbitmq-hello.gif" alt="Send and Receive applications in action, working with Amazon MQ RabbitMQ." /></p>
<p>Send and Receive applications in action, working with Amazon MQ RabbitMQ.</p>
<p><code>Send &gt;</code> 'Have a great day 😀'</p>
]]></content:encoded></item><item><title><![CDATA[How To Easily Make Your .NET AWS Lambda Function Idempotent]]></title><description><![CDATA[Idempotency refers to the property of a function that produces the same result regardless of how many times it is executed with the same input parameters.
Idempotency is particularly important in distributed systems, where messages may be retried due...]]></description><link>https://rahulpnath.hashnode.dev/idempotent-lambda-functions-dotnet</link><guid isPermaLink="true">https://rahulpnath.hashnode.dev/idempotent-lambda-functions-dotnet</guid><category><![CDATA[dotnet]]></category><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><dc:creator><![CDATA[Rahul Nath]]></dc:creator><pubDate>Thu, 01 Feb 2024 05:08:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1706763980387/ec1896ce-29ec-4bb6-8d4f-8d8b842ecf01.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Idempotency refers to the property of a function that produces the same result regardless of how many times it is executed with the same input parameters.</p>
<p>Idempotency is particularly important in distributed systems, where messages may be retried due to failures, timeouts, or other issues. Idempotency ensures the system remains consistent without unintended side effects, even if a request is duplicated or repeated.</p>
<p>The <a target="_blank" href="https://docs.powertools.aws.dev/lambda/dotnet/utilities/idempotency/">Powertools Idempotency utility</a> makes it easy to convert your Lambda functions into idempotent operations that are safe to retry.</p>
<p>In this blog post, let's learn how to get started using the Powertools Idempotency package, some key features, and how it easily fits into your existing Lambda Functions.</p>
<h2 id="heading-net-lambda-function-and-powertools-idempotency-package">.NET Lambda Function and Powertools Idempotency Package</h2>
<p>The Lambda Powertools Idempotency utility prevents the function handler from executing more than once for the same event payload during a specified period while ensuring it returns the same result.</p>
<p>The utility package is highly customizable, making it very easy to be tuned to work for your application-specific needs.</p>
<h3 id="heading-setting-up-lambda-function-idempotency">Setting Up Lambda Function Idempotency</h3>
<p>To start using the Idempotency utility, let's first install the NuGet package - <code>AWS.Lambda.Powertools.Idempotency</code></p>
<p>Once installed, we only need to add the <code>Idempotent</code> attribute to our Lambda Function handler endpoint, as shown below.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">LambdaFunction(Role = <span class="hljs-meta-string">"@WeatherApiLambdaExecutionRole"</span>)</span>]
[<span class="hljs-meta">HttpApi(LambdaHttpMethod.Post, <span class="hljs-meta-string">"/weather-forecast"</span>)</span>]
[<span class="hljs-meta">Idempotent</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">AddWeatherData</span>(<span class="hljs-params">[FromBody] WeatherForecast weatherForecast</span>)</span>
{
    Console.WriteLine(<span class="hljs-string">"Running time consuming process"</span>);
    <span class="hljs-keyword">await</span> Task.Delay(TimeSpan.FromSeconds(<span class="hljs-number">5</span>));
    <span class="hljs-keyword">await</span> dynamoDBContext.SaveAsync(weatherForecast);
}
</code></pre>
<p>The Lambda Function sample uses <a target="_blank" href="https://www.rahulpnath.com/blog/lambda-annotation-framework-crud-api/">Lambda Annotations Framework</a> , which makes it extremely easy to build Lambda Functions in .NET.</p>
<p>The function is an API endpoint that takes in a <code>WeatherForecast</code> data from the body of the API Gateway API request object.</p>
<p>In the Function constructor, you can initialize the Idempotency configuration. The code below sets up the package to use the DynamoDB table <code>IdempotencyTable</code> , as it's storage mechanism to maintain request/response information.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Functions</span>(<span class="hljs-params">IDynamoDBContext dynamoDBContext</span>)</span>
{
    <span class="hljs-keyword">this</span>.dynamoDBContext = dynamoDBContext;
    Idempotency.Configure(builder =&gt; builder.UseDynamoDb(<span class="hljs-string">"IdempotencyTable"</span>));
}
</code></pre>
<p><em>Ensure that the Lambda Function has appropriate rights to talk to the</em> <code>IdempotencyTable</code><em>.</em></p>
<p>When making requests against the API endpoint, it creates a hash value based on the request object (in this case, the <code>WeatherForecast</code> object) and stores it in the DynamoDB table.</p>
<p>If the same request is made, it will find a matching item in the DynamoDB table with the hash key and return the recorded response from the earlier successful request.</p>
<p>The default timeout period is 1 hour, which means any request with the same payload during that period will get the recorded response. After the timeout, the record is deleted from the DynamoDB table, and the first new request with the same payload will invoke the Lambda Function again.</p>
<p><img src="https://www.rahulpnath.com/content/images/2023/12/image-2.png" alt /></p>
<p>Idempotency DynamoDB Table with records populated as we make requests in the API endpoint.</p>
<p>Since the sample function above does not return any value, the data property in the DynamoDB table is empty.</p>
<h2 id="heading-customizing-idempotency-key">Customizing Idempotency Key</h2>
<p>The Idempotency package automatically used the first parameter In the above function, it automatically used the first parameter of the Lambda Function as the Idempotency key.</p>
<p>If there is more than one parameter for the Lambda Function, you can explicitly mark the item to use as the Idempotency Key using the <code>IdempotencyKey</code> attribute.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">LambdaFunction(Role = <span class="hljs-meta-string">"@WeatherApiLambdaExecutionRole"</span>)</span>]
[<span class="hljs-meta">HttpApi(LambdaHttpMethod.Post, <span class="hljs-meta-string">"/weather-forecast"</span>)</span>]
[<span class="hljs-meta">Idempotent</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">AddWeatherData</span>(<span class="hljs-params">APIGatewayHttpApiV2ProxyRequest request, [IdempotencyKey][FromBody] WeatherForecast weatherForecast</span>)</span>
{
    Console.WriteLine(<span class="hljs-string">"Running time consuming process"</span>);
    <span class="hljs-keyword">await</span> Task.Delay(TimeSpan.FromSeconds(<span class="hljs-number">5</span>));
    <span class="hljs-keyword">await</span> dynamoDBContext.SaveAsync(weatherForecast);
}
</code></pre>
<p>You can further customize the IdempotencyKey to use specific properties from the object payload.</p>
<p>This helps to ensure that the idempotency key is not affected by properties in the request object that might change over multiple requests (e.g., HTTP headers, etc.).</p>
<p>The code below specifies the <code>CityName</code> and <code>Date</code> properties to be used for the Idempotency key, ignoring all other properties on the request object (<code>WeatherForecast</code>).</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Functions</span>(<span class="hljs-params">IDynamoDBContext dynamoDBContext</span>)</span>
{
    <span class="hljs-keyword">this</span>.dynamoDBContext = dynamoDBContext;
    Idempotency.Configure(builder =&gt;
        builder
        .WithOptions(optionsBuilder =&gt; 
              optionsBuilder.WithEventKeyJmesPath(<span class="hljs-string">"[CityName, Date]"</span>))
        .UseDynamoDb(<span class="hljs-string">"IdempotencyTable"</span>));
}
</code></pre>
<p>Any requests with the same <code>CityName</code> and <code>Date</code> property will be treated as an idempotent request during the timeout period.</p>
<h2 id="heading-adding-payload-validation-for-idempotency">Adding Payload Validation for Idempotency</h2>
<p>Now, in the case of the WeatherForecast payload, what if the Temperature amount is different between two consecutive requests?</p>
<p>Since we chose not to include that as part of the Idempotency key, it will respond with the response from the initial request for any subsequent request.</p>
<p>In these cases, you can either add the <code>Temperature</code> property to the Idempotency key or add in request validation to ensure that the Temperature property has the same value as the initial request.</p>
<p>The initialization code below sets up the property validation<code>TemperatureC</code> using the <code>WithPayloadValidationJmesPath</code> function.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Functions</span>(<span class="hljs-params">IDynamoDBContext dynamoDBContext</span>)</span>
{
    <span class="hljs-keyword">this</span>.dynamoDBContext = dynamoDBContext;
    Idempotency.Configure(builder =&gt;
        builder
        .WithOptions(optionsBuilder =&gt; optionsBuilder
            .WithEventKeyJmesPath(<span class="hljs-string">"[CityName, Date]"</span>)
            .WithPayloadValidationJmesPath(<span class="hljs-string">"TemperatureC"</span>))
        .UseDynamoDb(<span class="hljs-string">"IdempotencyTable"</span>));
}
</code></pre>
<p>Any subsequent requests with the same <code>CityName</code> and <code>Date</code> property, with a different <code>TemperatureC</code> property will throw an <code>IdempotencyValidationException.</code></p>
<p>I hope this helps you to get started using the Idempotency Lambda package.</p>
]]></content:encoded></item><item><title><![CDATA[Are You Using HttpClient in The Right Way?]]></title><description><![CDATA[When an ASP NET application needs to talk to an external service or API, it needs to make an HTTP Request.
When using ASP.NET to build an application, HTTP requests is made using an instance of the HttpClient class. An HttpClient class acts as a sess...]]></description><link>https://rahulpnath.hashnode.dev/are-you-using-httpclient-in-the-right-way</link><guid isPermaLink="true">https://rahulpnath.hashnode.dev/are-you-using-httpclient-in-the-right-way</guid><category><![CDATA[dotnet]]></category><category><![CDATA[dotnetcore]]></category><category><![CDATA[best practices]]></category><dc:creator><![CDATA[Rahul Nath]]></dc:creator><pubDate>Tue, 30 Jan 2024 05:37:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/yg8pLPTAY8M/upload/cf7cdc0afef63aa987ee80eda01ae188.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When an ASP NET application needs to talk to an external service or API, it needs to make an HTTP Request.</p>
<p>When using <a target="_blank" href="http://asp.NET">ASP.NET</a> to build an application, HTTP requests is made using an instance of the <a target="_blank" href="https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=netcore-3.1&amp;WT.mc_id=AZ-MVP-5003875">HttpClient</a> class. An HttpClient class acts as a session to send HTTP Requests. It is a collection of settings applied to all requests executed by that instance.</p>
<p>Using the .NET C# HttpClient might seem straightforward. However, some underlying issues go unnoticed until when the application is under a large load. It is also not the best time for you to figure out these issues.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/bAXZx0zOeCU">https://youtu.be/bAXZx0zOeCU</a></div>
<p> </p>
<p>So let's spend some time now and understand the proper way to use HttpClient class and avoid running into issues with it for your application.</p>
<h2 id="heading-common-issues-when-using-c-httpclient">Common Issues When Using C# HttpClient</h2>
<p>Before we go any further, let's first understand the common issues when using HttpClient and how to uncover them even when running on your local machine without any load.</p>
<p>Below I have a code sample used to talk to an external API, in this case, a <a target="_blank" href="http://www.weatherapi.com/">weather api</a>, to fetch weather details for a given city. The code instantiates a new instance of HttpClient, makes a GET request to the external API and returns the JSON response.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span>(<span class="hljs-keyword">var</span> httpClient = <span class="hljs-keyword">new</span> HttpClient())
{
    <span class="hljs-keyword">string</span> APIURL = <span class="hljs-string">$"http://api.weatherapi.com/v1/current.json?key=<span class="hljs-subst">{API_KEY}</span>&amp;q=<span class="hljs-subst">{cityName}</span>"</span>;
    <span class="hljs-keyword">var</span> response =  <span class="hljs-keyword">await</span> httpClient.GetAsync(APIURL);
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> response.Content.ReadAsStringAsync();
}
</code></pre>
<p>It works fine; happy days. Let's move on to the next feature!</p>
<h3 id="heading-socket-exhaustion">Socket Exhaustion</h3>
<p>But wait, let's take a second and fire up the command line. Let's see what's happening behind the scenes with the HttpClient and every execution of the above code.</p>
<p>We will use a popular command-line utility, <a target="_blank" href="https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/netstat">netstat</a>, to look at the network statistics. It displays all active connections and details of it. Since we want to filter it down by the connections to the Weather API, let's filter it down using the API's IP address.</p>
<p>Running <code>ping api.weather.com</code> returns the IP address we want - <code>185.190.83.2</code></p>
<p>Let’s use that to filter the records returned using the netstat command - <code>netstat -ano | findstr 185.190.83.2</code></p>
<p><img src="https://www.rahulpnath.com/content/images/httpclient_socket_exhausition.jpg" alt="Each request to the API opens a new socket and a connection to the external API. It leads to socket exhaustion problem." /></p>
<p>Every request to the API endpoint opens a new connection to the external API. As shown in the image above, you can see more network connections when running the <code>netstat</code> command after making requests to our API endpoint. Even after the HttpClient connection is disposed, it leaves the network connections in a TIME_WAIT state.</p>
<p><em>The TIME_WAIT state means the connection is closed on one side (ours), but we''re still waiting to see if any additional packets come in because of a delay in the network connection.</em></p>
<p>These connections will eventually get closed after a timeout. However, as you can see, if there are many requests to the API, we can soon run of sockets to create (one per connection), and the application will throw an exception. The worst thing is such issues rarely happen in local development or testing unless you perform a load test on the application.</p>
<h3 id="heading-dns-changes-not-reflecting">DNS Changes Not Reflecting</h3>
<p>If creating a new instance for every request is bad, the first solution that comes to our mind is the Singleton Pattern.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> HttpClient _httpClient;

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">WeatherForecastController</span>(<span class="hljs-params">ILogger&lt;WeatherForecastController&gt; logger</span>)</span>
{
    _logger = logger;
    <span class="hljs-keyword">if</span>(_httpClient == <span class="hljs-literal">null</span>)
        _httpClient =  <span class="hljs-keyword">new</span> HttpClient();
}
</code></pre>
<p>We can create a new instance of HttpClient and not dispose of for the application lifetime. In this case, we reuse the HttpClient instance, and so only one connection is maintained. It works fine as long as there are no DNS or other network-level changes to the external API's connection. If it happens, we will have to restart our API application to create a new HttpClient instance.</p>
<p>You can <a target="_blank" href="https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#issues-with-the-original-httpclient-class-available-in-net-core">read more about these issues</a> using the HttpClient class directly in the official Microsoft documentation.</p>
<p><a target="_blank" href="https://bit.ly/asp-net-core-series"><img src="https://www.rahulpnath.com/content/images/asp_net_core_banner.png" alt /></a></p>
<h2 id="heading-use-ihttpclientfactory-to-create-httpclient">Use IHttpClientFactory To Create HttpClient</h2>
<p>Now that we know the issues let’s see how to fix this. The simplest way is to inject the <code>IHttpClient Factory</code> and create a new HttpClient instance from it.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IHttpClientFactory _httpClientFactory;
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">WeatherForecastController</span>(<span class="hljs-params">IHttpClientFactory httpClientFactory</span>)</span>
{
     _httpClientFactory = httpClientFactory;
}

[<span class="hljs-meta">HttpGet</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;<span class="hljs-keyword">string</span>&gt; <span class="hljs-title">Get</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> cityName</span>)</span>
{
  <span class="hljs-keyword">var</span> httpClient = _httpClientFactory.CreateClient();
    <span class="hljs-keyword">string</span> APIURL = <span class="hljs-string">$"http://api.weatherapi.com/v1/current.json?key=<span class="hljs-subst">{API_KEY}</span>&amp;q=<span class="hljs-subst">{cityName}</span>"</span>;
    ...
}
</code></pre>
<p>To enable Dependency Injection of the <code>IHttpClientFactory</code> instance we need to make sure to call <code>services.AddHttpClient()</code> method in <code>ConfigureServices</code> method of <code>Startup.cs</code>.</p>
<p><img src="https://www.rahulpnath.com/content/images/httpclient_using_ihttpclientfactory.jpg" alt="Using IHttpClientFactory, even after making multiple calls, we see only one connection in the Established state when running netstat." /></p>
<p>Using the IHttpClientFactory <a target="_blank" href="https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#benefits-of-using-ihttpclientfactory?WT.mc_id=AZ-MVP-5003875">has several benefits</a>, including managing the lifetime of the network connections. Using the factory to create the client reuses connection from a connection pool, thereby not creating too many sockets. The connections are reused and automatically disposed to avoid DNS level issues.</p>
<p>If you are interested in learning more about how it works internally <a target="_blank" href="https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#benefits-of-using-ihttpclientfactory?WT.mc_id=AZ-MVP-5003875">checkout out this link here</a>.</p>
<h3 id="heading-consumption-patterns">Consumption Patterns</h3>
<p>There are different ways we can use IHttpClientFactory in our application code.</p>
<h4 id="heading-basic-usagehttpsdocsmicrosoftcomen-usaspnetcorefundamentalshttp-requestsviewaspnetcore-31ampwtmcidaz-mvp-5003875basic-usage"><a target="_blank" href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.1&amp;WT.mc_id=AZ-MVP-5003875#basic-usage">Basic Usage</a></h4>
<p>The above usage of IHttpClientFactory is referred to as <a target="_blank" href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.1&amp;WT.mc_id=AZ-MVP-5003875#basic-usage"><strong>Basic usage</strong></a>, by directly injecting the factory instance into the Controller or class that requires an HttpClient instance. It works perfectly fine.</p>
<p>However, often when we need to make connections to external services, we also need a set of associated configuration details like URL, secret keys, special request headers, etc. While you can inject the configuration setting and other information into the Controller, the container soon starts violating the Single Responsibility Principle (SRP).</p>
<h4 id="heading-named-clientshttpsdocsmicrosoftcomen-usaspnetcorefundamentalshttp-requestsviewaspnetcore-31ampwtmcidaz-mvp-5003875named-clients"><a target="_blank" href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.1&amp;WT.mc_id=AZ-MVP-5003875#named-clients">Named Clients</a></h4>
<p>When using Named clients, the HttpClient instance configurations can be specified while registering the service with the Dependency Injection container. Instead of just calling the <code>services.AddHttpClient()</code> method in <code>Startup.cs,</code> we can add a client with a name and associated configuration.</p>
<p>Below we have a client with the name '<em>weather</em>,' and it also configures the BaseAddress to use for the client.</p>
<pre><code class="lang-csharp">services.AddHttpClient(<span class="hljs-string">"weather"</span>, c =&gt;
{
    c.BaseAddress = <span class="hljs-keyword">new</span> Uri(<span class="hljs-string">"http://api.weatherapi.com/v1/current.json"</span>);
})
</code></pre>
<p>In the Controller class, when we need to create a new HttpClient, we can use the name to create a specific client.</p>
<pre><code class="lang-csharp">  <span class="hljs-keyword">var</span> httpClient = _httpClientFactory.CreateClient(<span class="hljs-string">"weather"</span>);
</code></pre>
<h4 id="heading-typed-clientshttpsdocsmicrosoftcomen-usaspnetcorefundamentalshttp-requestsviewaspnetcore-31ampwtmcidaz-mvp-5003875typed-clients"><a target="_blank" href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.1&amp;WT.mc_id=AZ-MVP-5003875#typed-clients">Typed clients</a></h4>
<p>In the above code, we still need to hardcode the 'weather' string in the Controller and manually create a HttpClient ourselves.</p>
<p>To avoid calling the <code>CreateClient</code> method explicitly, we can use the Typed client pattern.</p>
<p>The external API calls are refactored into a separate class (<code>WeatherService</code>) for this pattern. This new class takes a dependency on the HttpClient directly, as shown below.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IWeatherService</span>
{
    Task &lt; <span class="hljs-keyword">string</span> &gt; Get(<span class="hljs-keyword">string</span> cityName);
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">WeatherService</span>: <span class="hljs-title">IWeatherService</span>
{
    <span class="hljs-keyword">private</span> HttpClient _httpClient;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">WeatherService</span>(<span class="hljs-params">HttpClient httpClient</span>)</span>
    {
        _httpClient = httpClient;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task &lt; <span class="hljs-keyword">string</span> &gt; Get(<span class="hljs-keyword">string</span> cityName)
    {
        <span class="hljs-keyword">string</span> APIURL = $ <span class="hljs-string">"?key={API_KEY}&amp;q={cityName}"</span>;
        <span class="hljs-keyword">var</span> response = <span class="hljs-keyword">await</span> _httpClient.GetAsync(APIURL);
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> response.Content.ReadAsStringAsync();
    }
}
</code></pre>
<p>When adding the new class, <code>WeatherService</code> to the Dependency Injection container, we can apply the relevant configuration, as shown below.</p>
<pre><code class="lang-csharp">services.AddHttpClient&lt;IWeatherService,WeatherService&gt;(c =&gt; {
      c.BaseAddress = <span class="hljs-keyword">new</span> Uri(<span class="hljs-string">"http://api.weatherapi.com/v1/current.json"</span>);
})
</code></pre>
<p>The Controller class can now use the <code>WeatherService</code> and call it to get back the relevant data, as shown below.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">WeatherForecastController</span>(<span class="hljs-params">IWeatherService weatherService</span>)</span>
{
    _weatherService = weatherService;
}

[<span class="hljs-meta">HttpGet</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;<span class="hljs-keyword">string</span>&gt; <span class="hljs-title">Get</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> cityName</span>)</span>
{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> _weatherService.Get(cityName);
}
</code></pre>
<p>By using IHttpClientFactory, we can solve all the initial issues that we saw with instantiating the HttpClient instance directly. After refactoring it to the Typed client consumption pattern, it is also well separated and easier to maintain. It drives us to write cleaner, loosely coupled code.</p>
<p>Are you using HttpClient the right way? Yes, you are now.</p>
<p><strong>References:</strong></p>
<ul>
<li><p><a target="_blank" href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.1&amp;WT.mc_id=AZ-MVP-5003875#named-clients">Make HTTP requests using IHttpClientFactory in ASP.NET Core</a></p>
</li>
<li><p><a target="_blank" href="https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#how-to-use-typed-clients-with-ihttpclientfactory?WT.mc_id=AZ-MVP-5003875">Use IHttpClientFactory to implement resilient HTTP requests</a></p>
</li>
<li><p><a target="_blank" href="https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/">You're using HttpClient wrong and it's destabilizing your software</a></p>
</li>
</ul>
]]></content:encoded></item></channel></rss>