0

I am writing a service for sending emails and I would like to send multiple email notifications at the same time. What I currently have is this:

    private void SendInstantMailNotification(string notificationId)
    {
        MailMessage? message = null;
        var notifications = _dbContext
                .Notifications
                .Where(x => x.Id.Equals(notificationId))
                .ToList();

        var notification = notifications.First();

       message = notification.Content;

       Smtp.SendMailSync(message, SmtpConfiguration, Smtp.MailTypeEnum.HTML);
    }

The last line of the code creates an instance of the "SMTP" service. And for each time I would like to send an email a new instance is created. How do I achieve this to be only one instance to be created and called multiple times without overloading the system?

This is the constructor:

    private readonly NotificationQueueContext _dbContext;

    protected NotificationQueueService(NotificationQueueContext dbContext)
    {
        _dbContext = dbContext;
    }
10
  • can you include the constructor of the class in your question? I don't see where the method creates a new instance of SMTP Commented Jan 23, 2023 at 14:48
  • I added the constructor. What happens when you call a method like the method above? The Smtp.SendMailAsync method. Doesn't it get instantiated every time this is called? Commented Jan 23, 2023 at 14:50
  • still can not find where you have created your smtp class. something like this: SmtpClient Smtp = new SmtpClient("XXX") Commented Jan 23, 2023 at 14:55
  • 1
    You can achieve that by using Singleton Design Pattern in C#, look at this link, I hope it will be useful dotnettutorials.net/lesson/singleton-design-pattern Commented Jan 23, 2023 at 14:55
  • There is a dependency on the project which has the Smtp service. I can call it from that dependency. @Amin Commented Jan 23, 2023 at 14:58

1 Answer 1

1

As I understand, you need a mechanism to sequentially run some tasks. So I created a Background service which creates a SMTP client once and a ConcurrentQueue to hold the mail requests and run them one by one.

This service is going to be active through the whole process of your application so it has while(TRUE) in it. and after each email it sends it waits for 500 ms.

If you want to send a mail from other services you just need to call RegisterMailRequest to enqueue a mail request.

you should define this service as a HostedService like this: services.AddHostedService<SequentialJobHandler>();

using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using MailKit.Net.Smtp;
using Microsoft.Extensions.Hosting;
using MimeKit;

namespace Sample
{
    public class SequentialJobHandler : BackgroundService
    {
        private readonly string MailServerAddress;
        private readonly int MailServerPort;
        private readonly string AdminEmailAccount;
        private readonly string AdminEmailAccountPass;
        private readonly string MailUser;
        private readonly string MailTitle;
        private ConcurrentQueue<MailRequest> queue = new ConcurrentQueue<MailRequest>();


        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            using (var client = new SmtpClient())
            {
                // For demo-purposes, accept all SSL certificates (in case the server supports STARTTLS)
                client.ServerCertificateValidationCallback = (s, c, h, e) => true;

                await client.ConnectAsync(MailServerAddress, MailServerPort, MailKit.Security.SecureSocketOptions.Auto);

                // Note: only needed if the SMTP server requires authentication
                await client.AuthenticateAsync(MailUser, AdminEmailAccountPass);


                while (true)
                {
                    MailRequest localValue = null;
                    if (queue.TryDequeue(out localValue))
                    {
                        if (localValue != null)
                        {
                            SendMail(localValue, client);    
                        }
                    }
                    Thread.Sleep(500);
                }
                //await client.DisconnectAsync(true);
            }
        }


        private async Task SendMail(MailRequest request, SmtpClient client)
        {
            var message = new MimeMessage();
            message.From.Add(new MailboxAddress(MailTitle, AdminEmailAccount));
            message.To.Add(new MailboxAddress(request.toUsername,  request.toEmail));
            message.Subject = request.subject;

            message.Body = new TextPart("html")
            {
                Text = request.body
            };
            
            await client.SendAsync(message);
        }


        public void RegisterMailRequest(MailRequest request)
        {
            queue.Enqueue(request);
        }
        
        public class MailRequest
        {
            public string toUsername, toEmail, subject, body;
        }
    }
}

hope this helps.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.