<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Simone Riggi</title>
    <description>The latest articles on DEV Community by Simone Riggi (@simone_riggi_2dd293f5bd0b).</description>
    <link>https://dev.to/simone_riggi_2dd293f5bd0b</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3409690%2F8e0cff1c-88cd-4181-8345-7fc686030136.jpg</url>
      <title>DEV Community: Simone Riggi</title>
      <link>https://dev.to/simone_riggi_2dd293f5bd0b</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/simone_riggi_2dd293f5bd0b"/>
    <language>en</language>
    <item>
      <title>Mastering Task Cancellation in C#: A Practical Guide with Linked Tokens</title>
      <dc:creator>Simone Riggi</dc:creator>
      <pubDate>Fri, 05 Sep 2025 15:59:33 +0000</pubDate>
      <link>https://dev.to/simone_riggi_2dd293f5bd0b/mastering-task-cancellation-in-c-a-practical-guide-with-linked-tokens-309i</link>
      <guid>https://dev.to/simone_riggi_2dd293f5bd0b/mastering-task-cancellation-in-c-a-practical-guide-with-linked-tokens-309i</guid>
      <description>&lt;p&gt;Over the past few days, I worked on a microservice that heavily relied on asynchronous tasks, synchronized code sections, and monitoring jobs to keep track of the task status. While doing this, I struggled a bit to fully understand how the &lt;strong&gt;cancellation token pattern&lt;/strong&gt; works in C#. In this article, I’ll share what I learned.&lt;/p&gt;




&lt;h3&gt;
  
  
  Async Programming in C
&lt;/h3&gt;

&lt;p&gt;Async programming in C# is powerful—but as with great power comes great responsibility.&lt;br&gt;&lt;br&gt;
Long-running tasks can quickly become resource hogs if you don’t give them a way to stop when they’re no longer needed. That’s exactly where &lt;strong&gt;cancellation tokens&lt;/strong&gt; come in.&lt;/p&gt;

&lt;p&gt;In this guide, we’ll explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;How to use &lt;code&gt;CancellationToken&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to combine multiple cancellation sources with &lt;code&gt;CreateLinkedTokenSource()&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A practical demo: a fake &lt;strong&gt;file download manager&lt;/strong&gt; with workers that can be cancelled by the user or automatically when they timeout&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  Why Cancellation Matters
&lt;/h3&gt;

&lt;p&gt;Imagine starting a big file download—or any task that risks running forever in the background. Now imagine the user closes the app or decides to cancel. Without cancellation support, your tasks will continue running until completion, wasting CPU and memory.&lt;/p&gt;

&lt;p&gt;Cancellation tokens provide a &lt;strong&gt;cooperative mechanism&lt;/strong&gt; to signal when a task should stop.&lt;/p&gt;


&lt;h3&gt;
  
  
  A First Taste of Cancellation
&lt;/h3&gt;

&lt;p&gt;Our demo will have the following requirements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Start &lt;strong&gt;5 tasks&lt;/strong&gt; simulating file downloads&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each download should take roughly &lt;strong&gt;5–10 seconds&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Show progress in the console&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allow the user to cancel all downloads by pressing &lt;strong&gt;'c'&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Support a &lt;strong&gt;timeout simulation&lt;/strong&gt; on one of the tasks that cancels itself&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Print a success message if all tasks complete&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;externalCancellation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CancellationTokenSource&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Press 'c' to cancel within 3 seconds after work begins."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Or let the task time out by doing nothing."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;KeyChar&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;externalCancellation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Cancel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Wait 1 second before starting the main work&lt;/span&gt;
    &lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;workerIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// capture the current value&lt;/span&gt;
        &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;timerTimeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workerIndex&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="m"&gt;2000&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BackgroundDownloadWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workerIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timerTimeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DoWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;externalCancellation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main: All workers completed successfully."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AggregateException&lt;/span&gt; &lt;span class="n"&gt;ae&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ae&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InnerExceptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;OperationCanceledException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;externalCancellation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsCancellationRequested&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main: Operation cancelled by user."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;
                    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main: Operation cancelled due to timeout or other reasons."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Press a key to exit"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadKey&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;externalCancellation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Key Takeaways from the Main Program:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A separate task monitors for &lt;strong&gt;user cancellation&lt;/strong&gt; via key press.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;externalCancellation&lt;/code&gt; is used to cancel all running tasks when the user presses &lt;code&gt;'c'&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each worker executes a simulated file download, with a unique index for console logging.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When cancellation occurs, an &lt;code&gt;OperationCanceledException&lt;/code&gt; is thrown and caught, distinguishing between &lt;strong&gt;user-triggered&lt;/strong&gt; and &lt;strong&gt;internal&lt;/strong&gt; cancellations.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  BackgroundDownloadWorker
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BackgroundDownloadWorker&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;CancellationTokenSource&lt;/span&gt; &lt;span class="n"&gt;internalCancellation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CancellationTokenSource&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;internalCancellationToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_workerIndex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Timer&lt;/span&gt; &lt;span class="n"&gt;_timer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;BackgroundDownloadWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;timerTimeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_workerIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timerTimeout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;_timer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TimeoutAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timerTimeout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Timeout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Infinite&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DoWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;externalCancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;internalCancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;internalCancellation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;linkedCts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CancellationTokenSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateLinkedTokenSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;internalCancellationToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;externalCancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;DoInternalWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linkedCts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OperationCanceledException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;internalCancellationToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsCancellationRequested&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Worker &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_workerIndex&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:: download timeout"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;externalCancellationToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsCancellationRequested&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Worker &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_workerIndex&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:: download cancelled by user"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DoInternalWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;secondsOfWork&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_workerIndex&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;7000&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Worker &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_workerIndex&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:: downloading"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;secondsOfWork&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ThrowIfCancellationRequested&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Worker &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_workerIndex&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:: Working... &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; seconds elapsed."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Worker &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_workerIndex&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:: Download completed successfully."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;TimeoutAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Worker &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_workerIndex&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:: timeout expired"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;internalCancellation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Cancel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;_timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Fields
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;CancellationTokenSource&lt;/span&gt; &lt;span class="n"&gt;internalCancellation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CancellationTokenSource&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;internalCancellationToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_workerIndex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Timer&lt;/span&gt; &lt;span class="n"&gt;_timer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;internalCancellation&lt;/em&gt;: A CancellationTokenSource that allows this worker to cancel itself, e.g., on timeout.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;internalCancellationToken&lt;/em&gt;: Holds the CancellationToken from internalCancellation, to be used for checking if the task was canceled internally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;_&lt;em&gt;workerIndex&lt;/em&gt;: Stores the worker’s index, mainly for logging purposes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;_&lt;em&gt;timer&lt;/em&gt;: A System.Threading.Timer to enforce a timeout on the work&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2. Constructor
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;BackgroundDownloadWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;timerTimeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_workerIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timerTimeout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;_timer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TimeoutAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timerTimeout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Timeout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Infinite&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;index&lt;/em&gt;: Used to differentiate workers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;timerTimeout&lt;/em&gt;: Optional parameter that sets a timeout in milliseconds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If a timeout is provided, a timer is created, which will call _TimeoutAction _when the timeout expires.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  3. Method
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DoWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;externalCancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;internalCancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;internalCancellation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;linkedCts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CancellationTokenSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateLinkedTokenSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;internalCancellationToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;externalCancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;DoInternalWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linkedCts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OperationCanceledException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;internalCancellationToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsCancellationRequested&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Worker &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_workerIndex&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:: download timeout"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;externalCancellationToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsCancellationRequested&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Worker &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_workerIndex&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:: download cancelled by user"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Accepts an external cancellation token, e.g., user requested cancellation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Combines internal and external tokens into a linked token. This allows cancellation to occur from either source.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Calls &lt;em&gt;DoInternalWork&lt;/em&gt;, which simulates the download.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Catches OperationCanceledException and logs the reason for cancellation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Internal → timeout&lt;/li&gt;
&lt;li&gt; External → user cancellation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Combining Multiple Cancellation Sources
&lt;/h3&gt;

&lt;p&gt;Sometimes cancellation can come from multiple sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;External cancellation&lt;/strong&gt; – user input, signals, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Internal cancellation&lt;/strong&gt; – timeout, watchdog, or business logic&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;C# provides a neat solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;linkedCts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CancellationTokenSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateLinkedTokenSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;internalCancellationToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;externalCancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;strong&gt;either token&lt;/strong&gt; is cancelled, the linked token triggers cancellation for the task.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. DoInternalWork Method
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DoInternalWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;secondsOfWork&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_workerIndex&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;7000&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Worker &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_workerIndex&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:: downloading"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;secondsOfWork&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ThrowIfCancellationRequested&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Worker &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_workerIndex&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:: Working... &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; seconds elapsed."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Worker &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_workerIndex&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:: Download completed successfully."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Simulates a download task by looping and sleeping 1 second at a time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;secondsOfWork&lt;/em&gt; is a rough duration for the task, based on _workerIndex.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Checks cancellationToken every second. If cancellation is requested, throws &lt;em&gt;OperationCanceledException&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Logs progress to the console.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. TimeoutAction Method
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;TimeoutAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Worker &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_workerIndex&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:: timeout expired"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;internalCancellation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Cancel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;_timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;called when the timer expires.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cancels the worker using internal cancellation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Disposes the timer to clean up resources.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Test Cases
&lt;/h3&gt;

&lt;p&gt;To see the cancellation pattern in action, you can test two main scenarios:&lt;/p&gt;

&lt;h4&gt;
  
  
  1️⃣ User Cancels with &lt;code&gt;'c'&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; The user decides to stop all downloads before they complete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Run the program.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When prompted, press &lt;code&gt;'c'&lt;/code&gt; within a few seconds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Observe the console logs.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Expected Behavior:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Each worker (1,3) prints a cancellation message&lt;/li&gt;
&lt;li&gt;  Worker (2) cancelled because of timeout expired&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhx75bmipost6175iu33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhx75bmipost6175iu33.png" alt="User cancels with 'c'" width="500" height="325"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h4&gt;
  
  
  2️⃣ Let the Program Run Without Interruption
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; The program is allowed to complete normally.&lt;br&gt;
&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Run the program.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do &lt;strong&gt;not&lt;/strong&gt; press &lt;code&gt;'c'&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wait for all workers to finish.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Expected Behavior:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Workers (1,3) complete their downloads successfully&lt;/li&gt;
&lt;li&gt;Worker (2) cancelled because of timeout expired&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcy0ef0oqyb9rw2m971jc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcy0ef0oqyb9rw2m971jc.png" alt="Let the program run" width="499" height="716"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In this article, we explored:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;How to use &lt;code&gt;CancellationToken&lt;/code&gt; to stop long-running tasks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to link multiple cancellation sources (user input + internal timeout)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A practical demo with multiple background workers&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern is extremely useful in real applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Cancelling HTTP requests when users navigate away&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stopping database queries after a timeout&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Managing parallel background jobs gracefully&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cancellation makes your applications &lt;strong&gt;more responsive, resource-friendly, and robust&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>coding</category>
      <category>microservices</category>
    </item>
    <item>
      <title>From .NET Framework on Windows to .NET 5 on Linux (AWS): what I migrated, how, and why</title>
      <dc:creator>Simone Riggi</dc:creator>
      <pubDate>Fri, 22 Aug 2025 14:19:53 +0000</pubDate>
      <link>https://dev.to/simone_riggi_2dd293f5bd0b/from-net-framework-on-windows-to-net-5-on-linux-aws-what-i-migrated-how-and-why-201k</link>
      <guid>https://dev.to/simone_riggi_2dd293f5bd0b/from-net-framework-on-windows-to-net-5-on-linux-aws-what-i-migrated-how-and-why-201k</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;I recently completed a deep migration that touched code, infrastructure, build, and runtime. The starting point was a &lt;strong&gt;Windows-only .NET Framework class library&lt;/strong&gt;, bundled into a monolithic deployment and manually hosted on a static server. It depended on &lt;strong&gt;Windows-specific executables/libraries&lt;/strong&gt;, had &lt;strong&gt;hard-coded configuration&lt;/strong&gt;, no &lt;strong&gt;CI/CD&lt;/strong&gt;, and &lt;strong&gt;no elasticity&lt;/strong&gt;.&lt;br&gt;
The target was a &lt;strong&gt;cloud-native, Linux-hosted .NET 5 Web API&lt;/strong&gt; running on EC2 behind an &lt;strong&gt;Auto Scaling Group&lt;/strong&gt;, configured via &lt;strong&gt;AWS Systems Manager Parameter Store&lt;/strong&gt;, managed by &lt;strong&gt;systemd&lt;/strong&gt;, and deployed automatically through &lt;strong&gt;TeamCity&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: I had to use .NET 5 ** to guarantee the backward compatibility of legacy code and third-party libraries** not compatible with .NET6+.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Why the change?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cost&lt;/strong&gt;: Linux vs Windows hosting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modernization&lt;/strong&gt;: move toward .NET (Core) and decouple from Win32 APIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: ASG with rolling/instance refresh.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Velocity&lt;/strong&gt;: CI/CD, reproducible builds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reliability&lt;/strong&gt;: systemd supervision, health checks, structured logging.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Before → After
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;.NET Framework class library (MVC-era Web API glue).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Windows-only dependencies (EXEs/DLLs).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Manual deploys, single box, no auto-scaling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Config in &lt;code&gt;web.config&lt;/code&gt;/&lt;code&gt;app.config&lt;/code&gt;, minimal environment awareness.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;.NET 5 Web API (Kestrel), Linux-first.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Native dependencies replaced or repackaged for Linux.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CI/CD with TeamCity → S3 artifact → ASG instance refresh.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Config from &lt;strong&gt;Parameter Store&lt;/strong&gt; → &lt;code&gt;appsettings.json&lt;/code&gt; at boot.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Service managed by &lt;strong&gt;systemd&lt;/strong&gt; with logs in &lt;code&gt;journalctl&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzd7uoyyxl2z7sil1vmrm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzd7uoyyxl2z7sil1vmrm.png" alt=" " width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Step 1 — Port to .NET 5 Web API
&lt;/h1&gt;

&lt;p&gt;I created a new .NET 5 Web API host and &lt;strong&gt;moved all registration/configuration&lt;/strong&gt; (Autofac, Swagger, formatters, handlers) into &lt;code&gt;Startup&lt;/code&gt;/&lt;code&gt;Program&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nf"&gt;CreateHostBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IHostBuilder&lt;/span&gt; &lt;span class="nf"&gt;CreateHostBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateDefaultBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureWebHostDefaults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Listen on any IP; can be overridden with ASPNETCORE_URLS or --urls&lt;/span&gt;
                &lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseKestrel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                   &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseStartup&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Startup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Handlers → Middleware&lt;/strong&gt;: old &lt;code&gt;DelegatingHandler&lt;/code&gt;/&lt;code&gt;MessageHandler&lt;/code&gt; logic became ASP.NET Core middleware.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Configuration&lt;/strong&gt;: moved from &lt;code&gt;AppSettings.config&lt;/code&gt; to &lt;code&gt;appsettings.json&lt;/code&gt; + &lt;code&gt;IConfiguration/IOptions&amp;lt;T&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Swagger&lt;/strong&gt;: re-registered via &lt;code&gt;AddSwaggerGen()&lt;/code&gt; and &lt;code&gt;UseSwaggerUI()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: .NET 5 &lt;strong&gt;does not support implicit global usings&lt;/strong&gt; (that came in .NET 6). Keep your usings explicit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Step 2 — Make it build and run on Linux
&lt;/h1&gt;

&lt;p&gt;Set the &lt;strong&gt;runtime identifier&lt;/strong&gt; and (optionally) self-contained publish in the Web API &lt;code&gt;.csproj&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PropertyGroup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TargetFramework&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;net5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;TargetFramework&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RuntimeIdentifier&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;linux&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x64&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;RuntimeIdentifier&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PublishSingleFile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;PublishSingleFile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SelfContained&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;SelfContained&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;InvariantGlobalization&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;InvariantGlobalization&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;PropertyGroup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet publish -c Release -r linux-x64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: If you use third-party libraries, ensure the &lt;strong&gt;Linux&lt;/strong&gt; variants are included next to your binary and have &lt;strong&gt;execute&lt;/strong&gt; permission where required.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 3 — Handle Windows-only dependencies
&lt;/h1&gt;

&lt;p&gt;Some functionality relied on Windows executables (e.g., structured output converters). On Linux:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Replace with &lt;strong&gt;vendor’s Linux binaries&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensure they’re part of the artifact (mark files as &lt;em&gt;Copy to Output Directory&lt;/em&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mark them executable&lt;/strong&gt; at deploy time:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chmod +x /var/opt/net-webapi/Libraries/library
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a native &lt;code&gt;.so&lt;/code&gt; must be found at runtime, export &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; in the service unit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Environment=LD_LIBRARY_PATH=/var/opt/net-webapi/Libraries:$LD_LIBRARY_PATH

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

&lt;/div&gt;



&lt;p&gt;Also normalize &lt;strong&gt;path separators&lt;/strong&gt; in code; prefer &lt;code&gt;Path.Combine&lt;/code&gt; and avoid &lt;code&gt;C:\...&lt;/code&gt; style paths.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 4 — Service on Linux with systemd
&lt;/h1&gt;

&lt;p&gt;Create and add &lt;code&gt;net-webapi.service&lt;/code&gt; in the .NET project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight postscript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;Description=WebAPI&lt;/span&gt; &lt;span class="nf"&gt;service&lt;/span&gt; 
&lt;span class="nf"&gt;After=network.target&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;WorkingDirectory=&lt;/span&gt;&lt;span class="nv"&gt;/var/opt/net-webapi&lt;/span&gt;
&lt;span class="nf"&gt;ExecStart=&lt;/span&gt;&lt;span class="nv"&gt;/var/opt/net-webapi/NET.WebAPI&lt;/span&gt; &lt;span class="nf"&gt;--urls&lt;/span&gt; &lt;span class="nf"&gt;http:&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;/0.0.0.0:80&lt;/span&gt;
&lt;span class="nf"&gt;Restart=always&lt;/span&gt;
&lt;span class="nf"&gt;RestartSec=10&lt;/span&gt;
&lt;span class="nf"&gt;KillSignal=SIGINT&lt;/span&gt;
&lt;span class="nf"&gt;SyslogIdentifier=net-webapi&lt;/span&gt;
&lt;span class="nf"&gt;User=www-data&lt;/span&gt;
&lt;span class="nf"&gt;Group=www-data&lt;/span&gt;
&lt;span class="nf"&gt;Environment=ASPNETCORE_ENVIRONMENT=Production&lt;/span&gt;
&lt;span class="nf"&gt;#&lt;/span&gt; &lt;span class="nf"&gt;Optional:&lt;/span&gt; &lt;span class="kr"&gt;for&lt;/span&gt; &lt;span class="nf"&gt;native&lt;/span&gt; &lt;span class="nf"&gt;libs&lt;/span&gt; &lt;span class="err"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;external&lt;/span&gt; &lt;span class="nf"&gt;tools&lt;/span&gt;
&lt;span class="nf"&gt;Environment=LD_LIBRARY_PATH=&lt;/span&gt;&lt;span class="nv"&gt;/var/opt/net-webapi/lib:$LD_LIBRARY_PATH&lt;/span&gt;
&lt;span class="nf"&gt;#&lt;/span&gt; &lt;span class="nf"&gt;Allow&lt;/span&gt; &lt;span class="nf"&gt;www-data&lt;/span&gt; &lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nf"&gt;:80&lt;/span&gt; &lt;span class="nf"&gt;without&lt;/span&gt; &lt;span class="nf"&gt;root&lt;/span&gt; &lt;span class="s"&gt;(or use a reverse proxy)&lt;/span&gt;
&lt;span class="nf"&gt;AmbientCapabilities=CAP_NET_BIND_SERVICE&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Install&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;WantedBy=multi-user.targe&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Step 5 — CI/CD with TeamCity → S3 → ASG refresh
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Artifacts&lt;/strong&gt; (example):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;noodls.utilities/NET.WebAPI/bin/%Configuration%/net5.0/linux-x64 =&amp;gt; NET/NET.WebAPI

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Upload to S3&lt;/strong&gt; (build step):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;artifactsPath&amp;gt; =&amp;gt; &amp;lt;bucket&amp;gt;/&amp;lt;env&amp;gt;/NET/NET.WebAPI/package.zip
&amp;lt;artifactsPath&amp;gt; =&amp;gt; &amp;lt;bucket&amp;gt;/&amp;lt;env&amp;gt;/NET/NET.WebAPI/PreviousVersions/package_%build.number%.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Instance refresh&lt;/strong&gt; (PowerShell in TeamCity agent, or Bash):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight postscript"&gt;&lt;code&gt;&lt;span class="nf"&gt;$envs&lt;/span&gt; &lt;span class="nf"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;"&lt;/span&gt;&lt;span class="c1"&gt;%AutoscalingGroups%".Split([Environment]::NewLine)&lt;/span&gt;
&lt;span class="nf"&gt;foreach&lt;/span&gt;&lt;span class="s"&gt;($env in $envs)&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;$parts&lt;/span&gt; &lt;span class="nf"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$env.Split&lt;/span&gt;&lt;span class="s"&gt;(';')&lt;/span&gt;
  &lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="s"&gt;($parts.Count -lt 2)&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;continue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;$asg&lt;/span&gt; &lt;span class="nf"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.Trim&lt;/span&gt;&lt;span class="s"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;;&lt;/span&gt; &lt;span class="nf"&gt;$region&lt;/span&gt; &lt;span class="nf"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.Trim&lt;/span&gt;&lt;span class="s"&gt;()&lt;/span&gt;
  &lt;span class="nf"&gt;aws&lt;/span&gt; &lt;span class="nf"&gt;autoscaling&lt;/span&gt; &lt;span class="nf"&gt;start-instance-refresh&lt;/span&gt; &lt;span class="nf"&gt;`&lt;/span&gt;
    &lt;span class="nf"&gt;--auto-scaling-group-name&lt;/span&gt; &lt;span class="nf"&gt;$asg&lt;/span&gt; &lt;span class="nf"&gt;`&lt;/span&gt;
    &lt;span class="nf"&gt;--preferences&lt;/span&gt; &lt;span class="nf"&gt;'&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;\"InstanceWarmup\":300,\"MinHealthyPercentage\":100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="nf"&gt;'&lt;/span&gt; &lt;span class="nf"&gt;`&lt;/span&gt;
    &lt;span class="nf"&gt;--region&lt;/span&gt; &lt;span class="nf"&gt;$region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Example variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ASG-NET-WEBAPI;us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Step 6 — Launch Template &amp;amp; User Data (EC2)
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Launch template&lt;/strong&gt; highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AMI&lt;/strong&gt;: choose a Linux image compatible with your runtime.&lt;br&gt;&lt;br&gt;
For .NET 5 (OpenSSL 1.1), &lt;strong&gt;Ubuntu 20.04 (focal)&lt;/strong&gt; or &lt;strong&gt;Amazon Linux 2&lt;/strong&gt; are safer than 22.04+ unless you add &lt;code&gt;libssl1.1&lt;/code&gt;. Better yet, plan to &lt;strong&gt;upgrade to .NET 8&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;IAM Role&lt;/strong&gt;: allow &lt;code&gt;ssm:GetParameter&lt;/code&gt; and S3 read.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Instance metadata&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Enable metadata &amp;amp; &lt;strong&gt;allow tags in metadata&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Tags&lt;/strong&gt; (key/value):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;appsettings-parameter-name = net-webapi.appsettings&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;log4net-parameter-name = net-webapi.log4net&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;User Data&lt;/strong&gt; (hardened &amp;amp; corrected):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight postscript"&gt;&lt;code&gt;&lt;span class="nf"&gt;#!&lt;/span&gt;&lt;span class="nv"&gt;/bin/bash&lt;/span&gt;
&lt;span class="nf"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;-euo&lt;/span&gt; &lt;span class="nf"&gt;pipefail&lt;/span&gt;

&lt;span class="nf"&gt;#&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt; &lt;span class="nf"&gt;base&lt;/span&gt;
&lt;span class="nf"&gt;apt-get&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt; &lt;span class="nf"&gt;-y&lt;/span&gt;
&lt;span class="nf"&gt;apt-get&lt;/span&gt; &lt;span class="nf"&gt;install&lt;/span&gt; &lt;span class="nf"&gt;-y&lt;/span&gt; &lt;span class="nf"&gt;unzip&lt;/span&gt; &lt;span class="nf"&gt;curl&lt;/span&gt; &lt;span class="nf"&gt;python3&lt;/span&gt; &lt;span class="nf"&gt;jq&lt;/span&gt;

&lt;span class="nf"&gt;#&lt;/span&gt; &lt;span class="nf"&gt;Install&lt;/span&gt; &lt;span class="nf"&gt;AWS&lt;/span&gt; &lt;span class="nf"&gt;CLI&lt;/span&gt; &lt;span class="nf"&gt;v2&lt;/span&gt;
&lt;span class="nf"&gt;curl&lt;/span&gt; &lt;span class="nf"&gt;-sS&lt;/span&gt; &lt;span class="nf"&gt;"https:&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;/awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"&lt;/span&gt; &lt;span class="nf"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;/tmp/awscliv2.zip&lt;/span&gt;
&lt;span class="nf"&gt;unzip&lt;/span&gt; &lt;span class="nf"&gt;-q&lt;/span&gt; &lt;span class="nv"&gt;/tmp/awscliv2.zip&lt;/span&gt; &lt;span class="nf"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;/tmp&lt;/span&gt;
&lt;span class="nv"&gt;/tmp/aws/install&lt;/span&gt;

&lt;span class="nf"&gt;#&lt;/span&gt; &lt;span class="s"&gt;(Optional)&lt;/span&gt; &lt;span class="nf"&gt;Install&lt;/span&gt; &lt;span class="nf"&gt;chrony&lt;/span&gt;
&lt;span class="nf"&gt;apt-get&lt;/span&gt; &lt;span class="nf"&gt;install&lt;/span&gt; &lt;span class="nf"&gt;-y&lt;/span&gt; &lt;span class="nf"&gt;chrony&lt;/span&gt;

&lt;span class="nf"&gt;#&lt;/span&gt; &lt;span class="nf"&gt;Prepare&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt; &lt;span class="nf"&gt;folder&lt;/span&gt;
&lt;span class="nf"&gt;install&lt;/span&gt; &lt;span class="nf"&gt;-d&lt;/span&gt; &lt;span class="nf"&gt;-o&lt;/span&gt; &lt;span class="nf"&gt;www-data&lt;/span&gt; &lt;span class="nf"&gt;-g&lt;/span&gt; &lt;span class="nf"&gt;www-data&lt;/span&gt; &lt;span class="nv"&gt;/var/opt/net-webapi&lt;/span&gt;

&lt;span class="nf"&gt;#&lt;/span&gt; &lt;span class="nf"&gt;Download&lt;/span&gt; &lt;span class="nf"&gt;package&lt;/span&gt; &lt;span class="nf"&gt;from&lt;/span&gt; &lt;span class="nf"&gt;S3&lt;/span&gt;
&lt;span class="nf"&gt;aws&lt;/span&gt; &lt;span class="nf"&gt;s3&lt;/span&gt; &lt;span class="nf"&gt;cp&lt;/span&gt; &lt;span class="nf"&gt;"s3:&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nf"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nf"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;/NET/NET.WebAPI/package.zip"&lt;/span&gt; &lt;span class="nv"&gt;/tmp/package.zip&lt;/span&gt;
&lt;span class="nf"&gt;unzip&lt;/span&gt; &lt;span class="nf"&gt;-q&lt;/span&gt; &lt;span class="nv"&gt;/tmp/package.zip&lt;/span&gt; &lt;span class="nf"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;/var/opt/net-webapi&lt;/span&gt;
&lt;span class="nf"&gt;chown&lt;/span&gt; &lt;span class="nf"&gt;-R&lt;/span&gt; &lt;span class="nf"&gt;www-data:www-data&lt;/span&gt; &lt;span class="nv"&gt;/var/opt/net-webapi&lt;/span&gt;
&lt;span class="nf"&gt;chmod&lt;/span&gt; &lt;span class="nf"&gt;+x&lt;/span&gt; &lt;span class="nv"&gt;/var/opt/net-webapi/NET.WebAPI&lt;/span&gt; &lt;span class="nf"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="nf"&gt;#&lt;/span&gt; &lt;span class="nf"&gt;If&lt;/span&gt; &lt;span class="nf"&gt;you&lt;/span&gt; &lt;span class="nf"&gt;ship&lt;/span&gt; &lt;span class="nf"&gt;native&lt;/span&gt; &lt;span class="nf"&gt;tools,&lt;/span&gt; &lt;span class="nf"&gt;ensure&lt;/span&gt; &lt;span class="nf"&gt;they're&lt;/span&gt; &lt;span class="nf"&gt;executable&lt;/span&gt;
&lt;span class="nf"&gt;#&lt;/span&gt; &lt;span class="nf"&gt;chmod&lt;/span&gt; &lt;span class="nf"&gt;+x&lt;/span&gt; &lt;span class="nv"&gt;/var/opt/net-webapi/Exporters/AprysePDFNet/lib/StructuredOutput&lt;/span&gt; &lt;span class="nf"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="nf"&gt;#&lt;/span&gt; &lt;span class="nf"&gt;Fetch&lt;/span&gt; &lt;span class="nf"&gt;Parameter&lt;/span&gt; &lt;span class="nf"&gt;Store&lt;/span&gt; &lt;span class="nf"&gt;values&lt;/span&gt; &lt;span class="s"&gt;(instance tags carry the parameter names)&lt;/span&gt;
&lt;span class="nf"&gt;CONFIG_PARAM=$&lt;/span&gt;&lt;span class="s"&gt;(curl -fsS http://169.254.169.254/latest/meta-data/tags/instance/appsettings-parameter-name)&lt;/span&gt;
&lt;span class="nf"&gt;LOG4NET_PARAM=$&lt;/span&gt;&lt;span class="s"&gt;(curl -fsS http://169.254.169.254/latest/meta-data/tags/instance/log4net-parameter-name)&lt;/span&gt;

&lt;span class="nf"&gt;aws&lt;/span&gt; &lt;span class="nf"&gt;ssm&lt;/span&gt; &lt;span class="nf"&gt;get-parameter&lt;/span&gt; &lt;span class="nf"&gt;--name&lt;/span&gt; &lt;span class="nf"&gt;"$CONFIG_PARAM"&lt;/span&gt; &lt;span class="nf"&gt;--with-decryption&lt;/span&gt; &lt;span class="nf"&gt;--region&lt;/span&gt; &lt;span class="nf"&gt;us-east-1&lt;/span&gt; &lt;span class="nf"&gt;\&lt;/span&gt;
  &lt;span class="nf"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;jq&lt;/span&gt; &lt;span class="nf"&gt;-r&lt;/span&gt; &lt;span class="nf"&gt;'.Parameter.Value'&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;/var/opt/net-webapi/appsettings.json&lt;/span&gt;

&lt;span class="nf"&gt;aws&lt;/span&gt; &lt;span class="nf"&gt;ssm&lt;/span&gt; &lt;span class="nf"&gt;get-parameter&lt;/span&gt; &lt;span class="nf"&gt;--name&lt;/span&gt; &lt;span class="nf"&gt;"$LOG4NET_PARAM"&lt;/span&gt; &lt;span class="nf"&gt;--with-decryption&lt;/span&gt; &lt;span class="nf"&gt;--region&lt;/span&gt; &lt;span class="nf"&gt;us-east-1&lt;/span&gt; &lt;span class="nf"&gt;\&lt;/span&gt;
  &lt;span class="nf"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;jq&lt;/span&gt; &lt;span class="nf"&gt;-r&lt;/span&gt; &lt;span class="nf"&gt;'.Parameter.Value'&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;/var/opt/net-webapi/log4net.config&lt;/span&gt;

&lt;span class="nf"&gt;chown&lt;/span&gt; &lt;span class="nf"&gt;www-data:www-data&lt;/span&gt; &lt;span class="nv"&gt;/var/opt/net-webapi/appsettings.json&lt;/span&gt; &lt;span class="nv"&gt;/var/opt/net-webapi/log4net.config&lt;/span&gt;

&lt;span class="nf"&gt;#&lt;/span&gt; &lt;span class="nf"&gt;Install&lt;/span&gt; &lt;span class="nf"&gt;systemd&lt;/span&gt; &lt;span class="nf"&gt;unit&lt;/span&gt;
&lt;span class="nf"&gt;cp&lt;/span&gt; &lt;span class="nv"&gt;/var/opt/net-webapi/net-webapi.service&lt;/span&gt; &lt;span class="nv"&gt;/etc/systemd/system/net-webapi.service&lt;/span&gt;
&lt;span class="nf"&gt;systemctl&lt;/span&gt; &lt;span class="nf"&gt;daemon-reload&lt;/span&gt;
&lt;span class="nf"&gt;systemctl&lt;/span&gt; &lt;span class="nf"&gt;enable&lt;/span&gt; &lt;span class="nf"&gt;net-webapi&lt;/span&gt;
&lt;span class="nf"&gt;systemctl&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt; &lt;span class="nf"&gt;net-webapi&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Avoid weakening TLS&lt;/strong&gt; (e.g., forcing TLS 1.0 in &lt;code&gt;openssl.cnf&lt;/code&gt;). Instead, keep modern defaults and fix the database side or client library if you hit handshake issues.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 7 — Database connectivity (TLS gotchas)
&lt;/h1&gt;

&lt;p&gt;If you see errors like &lt;em&gt;“SSL/TLS handshake failed”&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Prefer &lt;strong&gt;&lt;code&gt;Microsoft.Data.SqlClient&lt;/code&gt;&lt;/strong&gt; in .NET, which has better TLS support than the legacy &lt;code&gt;System.Data.SqlClient&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensure SQL Server supports &lt;strong&gt;TLS 1.2+&lt;/strong&gt; with a proper certificate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As a &lt;strong&gt;temporary&lt;/strong&gt; diagnostic only, try &lt;code&gt;Encrypt=False&lt;/code&gt; or &lt;code&gt;TrustServerCertificate=True&lt;/code&gt; in the connection string to confirm TLS is the issue—then fix certificates and revert to secure defaults.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Step 8 — Observability &amp;amp; permissions
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use &lt;code&gt;journalctl -u net-webapi -f&lt;/code&gt; to tail logs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the app writes under &lt;code&gt;/var/opt/net-webapi/temp&lt;/code&gt;, ensure the folder exists and is writable by the service user:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;install -d -o www-data -g www-data /var/opt/net-webapi/temp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  For “&lt;strong&gt;Permission denied to execute external module&lt;/strong&gt;” errors, &lt;code&gt;chmod +x&lt;/code&gt; the binary and verify the directory mount options allow execution (&lt;code&gt;noexec&lt;/code&gt; would block it).&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Step 9 — TeamCity artifact hygiene
&lt;/h1&gt;

&lt;p&gt;Mark non-code files (&lt;strong&gt;native tools, configs, templates&lt;/strong&gt;) as &lt;strong&gt;“Copy to Output Directory” → “Copy if newer”&lt;/strong&gt; so they appear in the published output and the TeamCity artifact.&lt;/p&gt;




&lt;h1&gt;
  
  
  What I’d improve next
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Upgrade to .NET 8 LTS&lt;/strong&gt; (security &amp;amp; runtime support; avoids OpenSSL 1.1 headaches).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Health checks&lt;/strong&gt; (&lt;code&gt;/health&lt;/code&gt;) + &lt;strong&gt;Auto Scaling&lt;/strong&gt; based on ALB target health.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CodeDeploy&lt;/strong&gt; or &lt;strong&gt;SSM Distributor&lt;/strong&gt; instead of ad-hoc unzip for richer deployments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Secret management&lt;/strong&gt; via &lt;strong&gt;Parameter Store (SecureString)&lt;/strong&gt; or &lt;strong&gt;Secrets Manager&lt;/strong&gt; with IAM.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final notes
&lt;/h2&gt;

&lt;p&gt;This migration wasn’t just a “retarget the framework” exercise. It required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Reworking &lt;strong&gt;hosting&lt;/strong&gt; and &lt;strong&gt;middleware&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Replacing/porting &lt;strong&gt;Windows-specific dependencies&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Designing a &lt;strong&gt;Linux-friendly packaging&lt;/strong&gt; and &lt;strong&gt;service model&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Building &lt;strong&gt;CI/CD&lt;/strong&gt; and &lt;strong&gt;immutable infrastructure&lt;/strong&gt; via ASG + Launch Templates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tightening &lt;strong&gt;permissions&lt;/strong&gt;, &lt;strong&gt;TLS&lt;/strong&gt;, and &lt;strong&gt;observability&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>dotnet</category>
      <category>devops</category>
      <category>architecture</category>
    </item>
    <item>
      <title>The Developer Behind the Code: Growth, Anxiety, and Rediscovering Joy</title>
      <dc:creator>Simone Riggi</dc:creator>
      <pubDate>Sun, 03 Aug 2025 14:38:14 +0000</pubDate>
      <link>https://dev.to/simone_riggi_2dd293f5bd0b/the-first-and-hopefully-last-post-about-myself-5cd9</link>
      <guid>https://dev.to/simone_riggi_2dd293f5bd0b/the-first-and-hopefully-last-post-about-myself-5cd9</guid>
      <description>&lt;p&gt;I promise — this will be the first and last post about &lt;em&gt;me&lt;/em&gt;. From now on, I’ll try to keep things professional and focus on interesting topics.&lt;/p&gt;

&lt;p&gt;I’ve always admired people who can explain complex technical ideas, or even talk about themselves, in a way that’s simple, engaging, and clear.&lt;br&gt;&lt;br&gt;
Lately, I’ve started journaling everything I do: what I learn, small wins of the day or week — anything that helps me stay aware of my journey, where I started, and where I’m heading.&lt;/p&gt;

&lt;p&gt;Every morning, when I wake up, one of my first feelings is anxiety — the pressure to hit some goal of the day: read a book for 30 minutes, study one hour for a certification, journal, do something productive… and in the rush, I completely forget to breathe.&lt;/p&gt;

&lt;p&gt;I’m writing this mainly to help myself. I read somewhere that writing your thoughts down can help you “debug” yourself. Maybe it’ll help someone else too.&lt;/p&gt;

&lt;p&gt;Am I the only one obsessed with the idea of constant self-improvement? Every day feels like &lt;em&gt;the day&lt;/em&gt; — the day I must make progress, and if I do, I expect to see immediate results.&lt;br&gt;&lt;br&gt;
But that’s not how it works (unfortunately).&lt;/p&gt;




&lt;h2&gt;
  
  
  How It Started
&lt;/h2&gt;

&lt;p&gt;Six years ago, right after earning my Master’s degree in Computer Science and just before the pandemic hit, I started working as a Software Engineer at a consulting company.&lt;/p&gt;

&lt;p&gt;Honestly, back then I didn’t want to be a developer.&lt;br&gt;&lt;br&gt;
Just the thought of deadlines, hidden bugs, building UIs and apps… it gave me a headache. I dreamed of a classic “9-to-6” job — no stress, no crunch, no chaos.&lt;br&gt;&lt;br&gt;
(Please don’t judge me — I was young, and I’m human.)&lt;/p&gt;

&lt;p&gt;Anyway, if you’ve worked in consulting, you know the drill. You get hired, and then… you may wait. No clients. Just waiting.&lt;br&gt;&lt;br&gt;
Waiting for a project, waiting for a chance — &lt;em&gt;any&lt;/em&gt; chance.&lt;/p&gt;

&lt;p&gt;Luckily (yes, luckily), my first opportunity came in the form of a .NET Desktop application to develop with another colleague — both of us at our first job.&lt;/p&gt;

&lt;p&gt;And that was it — despite all my efforts to avoid coding, fate had other plans.&lt;/p&gt;

&lt;p&gt;Before I knew it, my life became a cycle of team meetings, client calls, demos, tasks, unit tests… and yes, deadlines.&lt;/p&gt;

&lt;p&gt;I’ll spare you the details of how hard that first experience was — the unrealistic expectations, the pressure, the learning curve — but I want to highlight two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I realized I liked that life.&lt;/li&gt;
&lt;li&gt;And surprisingly, I really liked .NET and C#.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It was a revelation. A real mind-blower.&lt;/p&gt;




&lt;h2&gt;
  
  
  From Confusion to Clarity
&lt;/h2&gt;

&lt;p&gt;I remember the early months vividly. I’d look in the mirror and think:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;“Really? You? Are you serious?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But despite the sleepless nights and a few scoldings from clients, I discovered something unexpected:&lt;br&gt;&lt;br&gt;
I was having fun. I was being creative.&lt;/p&gt;

&lt;p&gt;And it changed everything.&lt;br&gt;&lt;br&gt;
It was the one variable I hadn’t considered. Creativity.&lt;br&gt;&lt;br&gt;
It turned out to be the key to my career.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Illusion of Constant Growth
&lt;/h2&gt;

&lt;p&gt;At this point, you might expect a happy ending — that everything went smoothly from there, that I found my path and got better every day.&lt;/p&gt;

&lt;p&gt;Not quite.&lt;/p&gt;

&lt;p&gt;Once I found my way, another problem surfaced: the obsession with always improving.&lt;/p&gt;

&lt;p&gt;Some days I feel really anxious — maybe I’m stuck doing boring or repetitive tasks, and I start thinking,&lt;br&gt;&lt;br&gt;
&lt;strong&gt;“Everyone else is building something meaningful while I’m stuck here.”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
I don’t know if that’s true, but it’s probably more realistic to say that most people in tech aren’t changing the world every day.&lt;br&gt;&lt;br&gt;
Most of the time, we’re just facing challenges, fixing issues, and solving small problems — and over time, those little efforts can lead to something big.&lt;/p&gt;

&lt;p&gt;I started coding in my free time, thinking it would help me grow, land a better job, learn a new framework, or build a product or startup.&lt;br&gt;&lt;br&gt;
But recently, I realized something: that mindset doesn’t come from love for software engineering.&lt;br&gt;&lt;br&gt;
It comes from anxiety and unrealistic expectations.&lt;/p&gt;

&lt;p&gt;When your hobby becomes something that &lt;em&gt;must&lt;/em&gt; deliver results — a business, a skill, a product — then it stops being a hobby.&lt;/p&gt;

&lt;p&gt;So now, I’m trying to reset my mindset.&lt;br&gt;&lt;br&gt;
I want to code, study, or explore a topic just for the joy of it. For myself.&lt;/p&gt;

&lt;p&gt;It doesn’t have to be perfect. I can even suck.&lt;br&gt;&lt;br&gt;
And that’s fine — because in that space, in &lt;em&gt;my&lt;/em&gt; space, I feel free.&lt;br&gt;&lt;br&gt;
Free to experiment, take risks, create something useless, and enjoy the moment.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Work on Myself
&lt;/h2&gt;

&lt;p&gt;While writing this, I’m fully aware that shifting to this mindset isn’t easy.&lt;br&gt;&lt;br&gt;
It’s a daily process — journaling, analyzing habits, building new ones, letting go of others.&lt;/p&gt;

&lt;p&gt;It takes commitment.&lt;br&gt;&lt;br&gt;
Awareness.&lt;br&gt;&lt;br&gt;
And the ability to celebrate small wins every day.&lt;/p&gt;

&lt;p&gt;There’s probably no perfect recipe for becoming a great software engineer.&lt;br&gt;&lt;br&gt;
But I like to believe this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Even if the path seems boring or pointless some days, if you try to improve just 1% each day, eventually you’ll reach a tipping point — where all the knowledge and effort you’ve accumulated finally pays off and becomes something powerful.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Thanks for reading. If you’ve felt similar emotions in your journey, I’d love to hear your story too.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>sideprojects</category>
      <category>career</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
