<?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" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Vu’s Substack]]></title><description><![CDATA[My personal Substack]]></description><link>https://vuvtdhh.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!nPit!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25dcfafd-0079-4f33-9092-3411f5a5a975_1164x1168.jpeg</url><title>Vu’s Substack</title><link>https://vuvtdhh.substack.com</link></image><generator>Substack</generator><lastBuildDate>Thu, 21 May 2026 07:56:36 GMT</lastBuildDate><atom:link href="https://vuvtdhh.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Vu Van]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[vuvtdhh@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[vuvtdhh@substack.com]]></itunes:email><itunes:name><![CDATA[Vu Van]]></itunes:name></itunes:owner><itunes:author><![CDATA[Vu Van]]></itunes:author><googleplay:owner><![CDATA[vuvtdhh@substack.com]]></googleplay:owner><googleplay:email><![CDATA[vuvtdhh@substack.com]]></googleplay:email><googleplay:author><![CDATA[Vu Van]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Implementing an Email Queue with ASP.NET Using Hosted Service and Channel]]></title><description><![CDATA[A real-time email queue in ASP.NET]]></description><link>https://vuvtdhh.substack.com/p/implementing-an-email-queue-with-asp-net</link><guid isPermaLink="false">https://vuvtdhh.substack.com/p/implementing-an-email-queue-with-asp-net</guid><dc:creator><![CDATA[Vu Van]]></dc:creator><pubDate>Tue, 25 Jun 2024 18:38:30 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!pwa2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b3c409-23b4-4cff-820b-3ee8ac8ca03a_1684x875.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In modern web application development scenarios, designing an email queue for sending emails is quite common. </p><p>Imagine you need to design an email queue to receive emails that need to be sent from multiple sources (services, controllers,&#8230;) and send these emails using various senders.</p><p>To achieve this, you need to combine the email queue with a hosted service to create a real-time email sending service. This service will ensure that emails are processed and sent as soon as they enter the queue.</p><h3>Main tech stack:</h3><ol><li><p><strong>Hosted Service</strong>: A background service running alongside the main web application for handling tasks like sending emails.</p></li><li><p><strong>Channel&lt;T&gt;</strong>: A thread-safe, asynchronous data structure for passing data between producers and consumers, ideal for high-throughput scenarios.</p></li></ol><h3>Why Channel&lt;T&gt; instead of ConcurrentQueue&lt;T&gt;?</h3><p>In a high-throughput, multi-threaded environment, choosing the right data structure for handling concurrent tasks is crucial. While both <code>ConcurrentQueue&lt;T&gt;</code> and <code>Channel&lt;T&gt;</code> can handle multi-threaded scenarios, they have significant differences in their design and performance.</p><ol><li><p><strong>Thread-Safety</strong>: Both <code>ConcurrentQueue&lt;T&gt;</code> and <code>Channel&lt;T&gt;</code> are thread-safe, meaning they can handle multiple producers and consumers without data corruption. However, <code>Channel&lt;T&gt;</code> is optimized for asynchronous operations, which is crucial in web applications where responsiveness and resource management are key.</p></li><li><p><strong>Backpressure Handling</strong>: <code>Channel&lt;T&gt;</code> provides built-in support for backpressure, which helps manage the load when producers are generating items faster than consumers can process them. This ensures that your application remains stable under heavy load, preventing crashes and slowdowns.</p></li><li><p><strong>Asynchronous API</strong>: <code>Channel&lt;T&gt;</code> is designed with asynchronous programming in mind, making it easier to work with <code>async</code> and <code>await</code> patterns. This is particularly useful in ASP.NET Core applications where asynchronous operations are the norm.</p></li><li><p><strong>Performance</strong>: Channels can offer better performance in high-concurrency scenarios due to their design, which avoids some of the contention issues that can arise with <code>ConcurrentQueue&lt;T&gt;</code>. This means your email queue will be more efficient and can handle a higher volume of emails.</p></li><li><p><strong>Code Simplicity</strong>: Using <code>Channel&lt;T&gt;</code> can lead to simpler and more maintainable code. Since <code>Channel&lt;T&gt;</code> supports both bounded and unbounded modes, you can easily configure it to suit your application's needs without complex custom logic.</p></li></ol><p>In summary, while <code>ConcurrentQueue&lt;T&gt;</code> is a solid choice for many concurrent scenarios, <code>Channel&lt;T&gt;</code> offers additional benefits that make it more suitable for real-time applications and better integration with hosted services.</p><h3>Step 1: Creating a Simple Email Sender Class</h3><p>First, we'll create a simple class that simulates sending emails. This <code>EmailSender</code> class includes a method <code>SendEmailAsync</code> that mimics the delay of sending an email and then prints a message to the console.</p><pre><code>public class EmailSender
{
    public async Task SendEmailAsync(string email)
    {
        await Task.Delay(500);
        Console.WriteLine($"Email sent to: {email}");
    }
}</code></pre><h3>Step 2: Creating the Email Queue</h3><p>Next, we will create the <code>EmailQueue</code> class using <code>Channel&lt;string&gt;</code> to manage our email queue. This class will have methods to enqueue and dequeue emails.</p><pre><code>public class EmailQueue
{
    private readonly Channel&lt;string&gt; _channel;

    public EmailQueue()
    {
        _channel = Channel.CreateUnbounded&lt;string&gt;();
    }

    public async Task EnqueueAsync(string email)
    {
        await _channel.Writer.WriteAsync(email);
    }

    public async Task&lt;string&gt; DequeueAsync(CancellationToken cancellationToken)
    {
        return await _channel.Reader.ReadAsync(cancellationToken);
    }
}</code></pre><h3>Step 3: Creating the Hosted Service</h3><p>Now, let's implement the <code>EmailHostedService</code> class, which will act as a background service to continuously dequeue and send emails from the <code>EmailQueue</code>.</p><pre><code>public class EmailHostedService : BackgroundService
{
    private readonly EmailQueue _emailQueue;
    private readonly EmailSender _emailSender;

    public EmailHostedService(EmailQueue emailQueue, EmailSender emailSender)
    {
        _emailQueue = emailQueue ?? throw new ArgumentNullException(nameof(emailQueue));
        _emailSender = emailSender ?? throw new ArgumentNullException(nameof(emailSender));
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        // When using ConcurrentQueue directly, this while loop would continuously dequeue items without any delay.
        // Without appropriate delay mechanisms, this can lead to excessive CPU usage and potential application crashes.
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                var email = await _emailQueue.DequeueAsync(stoppingToken);
                await _emailSender.SendEmailAsync(email);
            }
            catch (Exception ex)
            {
                // Handle other exceptions
                Console.WriteLine($"Error sending email: {ex.Message}");
            }
        }
    }
}</code></pre><h3>Step 4: Registering Services</h3><p>Now, let's register the <code>EmailQueue</code>, <code>EmailSender</code>, and <code>EmailHostedService</code> with the <code>WebApplication</code> program in ASP.NET.</p><pre><code>var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();
builder.Services.AddSingleton&lt;EmailQueue&gt;();
builder.Services.AddSingleton&lt;EmailSender&gt;();
builder.Services.AddHostedService&lt;EmailHostedService&gt;();

var app = builder.Build();</code></pre><h3>Step 5: Creating a Controller Action to Queue Emails</h3><p>Now, let's create a controller action that queues emails using the <code>EmailQueue</code>.</p><pre><code>public class EmailController : ControllerBase
{
    private readonly EmailQueue _emailQueue;

    public EmailController(EmailQueue emailQueue)
    {
        _emailQueue = emailQueue;
    }

    public async Task&lt;IActionResult&gt; SendEmail(string email)
    {
        await _emailQueue.EnqueueAsync(email);
        return Ok("Email has been queued.");
    }
}</code></pre><h3>Here Are the Results</h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pwa2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b3c409-23b4-4cff-820b-3ee8ac8ca03a_1684x875.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pwa2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b3c409-23b4-4cff-820b-3ee8ac8ca03a_1684x875.gif 424w, https://substackcdn.com/image/fetch/$s_!pwa2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b3c409-23b4-4cff-820b-3ee8ac8ca03a_1684x875.gif 848w, https://substackcdn.com/image/fetch/$s_!pwa2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b3c409-23b4-4cff-820b-3ee8ac8ca03a_1684x875.gif 1272w, https://substackcdn.com/image/fetch/$s_!pwa2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b3c409-23b4-4cff-820b-3ee8ac8ca03a_1684x875.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pwa2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b3c409-23b4-4cff-820b-3ee8ac8ca03a_1684x875.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a3b3c409-23b4-4cff-820b-3ee8ac8ca03a_1684x875.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:380656,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pwa2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b3c409-23b4-4cff-820b-3ee8ac8ca03a_1684x875.gif 424w, https://substackcdn.com/image/fetch/$s_!pwa2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b3c409-23b4-4cff-820b-3ee8ac8ca03a_1684x875.gif 848w, https://substackcdn.com/image/fetch/$s_!pwa2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b3c409-23b4-4cff-820b-3ee8ac8ca03a_1684x875.gif 1272w, https://substackcdn.com/image/fetch/$s_!pwa2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3b3c409-23b4-4cff-820b-3ee8ac8ca03a_1684x875.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><strong>Source code:</strong> <a href="https://github.com/vuvtdhh/MailChannel">https://github.com/vuvtdhh/MailChannel</a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://vuvtdhh.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Vu&#8217;s Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Coming soon]]></title><description><![CDATA[This is Vu&#8217;s Substack.]]></description><link>https://vuvtdhh.substack.com/p/coming-soon</link><guid isPermaLink="false">https://vuvtdhh.substack.com/p/coming-soon</guid><dc:creator><![CDATA[Vu Van]]></dc:creator><pubDate>Wed, 31 Jan 2024 04:06:55 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!nPit!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25dcfafd-0079-4f33-9092-3411f5a5a975_1164x1168.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is Vu&#8217;s Substack.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://vuvtdhh.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://vuvtdhh.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item></channel></rss>