Home > other >  How can I adapt this code to use a single instance instead of creating multiple instances of a servi
How can I adapt this code to use a single instance instead of creating multiple instances of a servi

Time:01-24

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;
    }

CodePudding user response:

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.

  • Related