The project aims to develop a robust email interaction system leveraging MailKit for communication with mail servers and GreenMail for efficient testing purposes. By combining these technologies, the project ensures reliable email handling functionality alongside comprehensive testing capabilities.
Introduction
The project aims to develop a robust email interaction system leveraging MailKit for communication with mail servers and GreenMail for efficient testing purposes.
By combining these technologies, the project ensures reliable email handling functionality alongside comprehensive testing capabilities.
Project Source Code
You can find the project on this github page: GitHub
Key Components
MailKit Integration: Utilizing MailKit, the project establishes a reliable connection with external mail servers, enabling seamless transmission and retrieval of emails.
MailKit's comprehensive features facilitate smooth communication protocols, including SMTP, IMAP, and POP3, ensuring compatibility with various mail server configurations.
For detailed information on MailKit, visit their website: Official Page
GreenMail for Testing: GreenMail serves as the primary testing environment within the project, providing a lightweight, in-memory mail server solution. Its versatile capabilities allow for the simulation of various email scenarios, including sending, receiving, and manipulation of messages, all within a controlled testing environment. GreenMail's flexibility enables comprehensive testing of email functionalities, ensuring the robustness and reliability of the system.
For detailed information on GreenMail, visit their website: GreenMail Official Page
GreenMail Setup
This setup provides a convenient way to use GreenMail for testing email functionalities within a Docker environment.
To set up GreenMail using a Docker Compose file
greenmail:
container_name: greenmail
image: greenmail/standalone
ports:
- "3143:3143"
- "8080:8080"
- "3025:3025"
networks:
- greenmail
restart: unless-stopped
These setting allows GreenMail will be accessible via the following ports:
- SMTP: 3143
- HTTP (for the GreenMail web interface): 8080
- POP3: 3025
You can now interact with GreenMail for testing purposes.
Send Mail Flow with MailKit
The MailSenderService class within the Mail.Hub.Domain project encapsulates functionality for sending emails using the MailKit library. This service provides a simple and efficient way to send emails from within a .NET application.
public async Task SendMail(string body)
{
try
{
var message = new MimeMessage();
message.From.Add(new MailboxAddress("FromName", "fromAddress@gmail.com"));
message.To.Add(new MailboxAddress("test", "mytestmail@test.it"));
message.Subject = "test";
message.Body = new TextPart(MimeKit.Text.TextFormat.Html) { Text = $"<b>{body}</b>" };
using var client = new SmtpClient();
client.Connect("localhost", 3025, false);
client.Authenticate("test", "test");
await client.SendAsync(message);
client.Disconnect(true);
}
catch (Exception ex)
{
logger.LogError(ex, "Not work");
throw;
}
}
Code explanation:
- The SendMail method sends an email with the specified body content.
- The options required for sending the email, such as sender and recipient addresses, SMTP server details, and authentication credentials, are retrieved from the SenderMailOptions object injected into the service.
- The client is authenticated using the provided credentials, and the email is sent asynchronously using the SendAsync method.
Receive Mail Flow with MailKit
This ReceiverMailService class encapsulates the functionality to connect to the email server using the IMAP protocol, retrieve new emails, parse them, and handle them accordingly. It utilizes the MailKit library for IMAP operations.
The ParseNewMails method fetches new, unseen emails from the inbox, extracts relevant information such as subject and body, and sends it for further processing using the Mediator pattern.
public async Task ParseNewMails()
{
try
{
using var client = new ImapClient();
client.Connect(_options.Server, _options.Port, false);
client.Authenticate(_options.UserName, _options.Password);
_logger.LogInformation("Connected");
var inbox = client.Inbox;
inbox.Open(FolderAccess.ReadWrite);
_logger.LogInformation("Total messages: {0}", inbox.Count);
_logger.LogInformation("Recent messages: {0}", inbox.Recent);
var query = SearchQuery.Not(SearchQuery.Seen);
var messages = await inbox.SearchAsync(query);
foreach (var item in messages)
{
var message = await inbox.GetMessageAsync(item);
await _mediator.Send(new NewMailCommand() { Title = message.Subject, HtmlBody = message.HtmlBody });
inbox.AddFlags(item, MessageFlags.Seen, true);
}
client.Disconnect(true);
}
catch (Exception ex)
{
_logger.LogError(ex, "Not work");
throw;
}
}
For streamlining communication and promoting decoupling, MediatR serves as an indispensable tool. Learn more about MediatR and its benefits on the official GitHub repository: MediatR GitHub Repository
You can modify await _mediator.Send(...) line with a private method contains.
Next, let's examine how we integrate this email parsing functionality with Quartz.NET for scheduling:
public class IncomeMailsJob : IJob
{
private readonly ILogger<IncomeMailsJob> _logger;
private readonly IReceiverMailService _reviceMailService;
public IncomeMailsJob(ILogger<IncomeMailsJob> logger, IReceiverMailService reviceMailService)
{
_logger = logger;
_reviceMailService = reviceMailService;
}
public async Task Execute(IJobExecutionContext context)
{
_logger.LogInformation($"{nameof(IncomeMailsJob)} - Execution Start");
try
{
await _reviceMailService.ParseNewMails();
}
catch (Exception e)
{
_logger.LogError(e, $"{nameof(IncomeMailsJob)} - Execution Stop");
}
_logger.LogInformation($"{nameof(IncomeMailsJob)} - Execution Stop");
}
}
The IncomeMailsJob class represents a Quartz.NET job responsible for triggering the email parsing process at specified intervals. In the Execute method, it simply invokes the ParseNewMails method of the ReceiverMailService. Any exceptions during execution are logged for debugging purposes.
Finally, let's see how we set up the Quartz.NET job within the application's service configuration.
Generic quarz Job Registration:
public static void AddJobAndTrigger<T>(
this IServiceCollectionQuartzConfigurator quartz,
IConfiguration config)
where T : IJob
{
string jobName = typeof(T).Name;
var cronSchedule = config[jobName];
if (string.IsNullOrEmpty(cronSchedule))
{
throw new Exception($"No Quartz.NET Cron schedule found for job in configuration at {jobName}");
}
var jobKey = new JobKey(jobName);
quartz.AddJob<T>(opts => opts.WithIdentity(jobKey));
quartz.AddTrigger(opts => opts
.ForJob(jobKey)
.WithIdentity(jobName + "-trigger")
.WithCronSchedule(cronSchedule));
}
Using it you can easly add a Quartz job:
services.AddQuartz(q =>
{
q.AddJobAndTrigger<IncomeMailsJob>(configuration);
});
In this method, we register the necessary services, configure options for email settings, register MediatR for handling commands, configure Quartz.NET to schedule the IncomeMailsJob, and finally start the Quartz.NET scheduler as a hosted service.