So this is an automatic email sending application, it reads with Handlebar the email templates and assembles them, and then sends the email classes.
My goal with the code is to make it as easy to unit-test as possible, I haven't added any unit tests yet, but I plan to, I just need to learn a bit more unit-testing with simpler things.
What can be improved with the code? Should I use generic, create a email Collection class, a way to change the smtp settings in .config? Any tips is helpful, but please keep it as simple as possible. As I'm still very much new to programming. Maybe I shouldn´t use the temp smtp server like this, which will make it harder to setup a real one?
The code has been tested by setting up a temporary smtp server at mailtrap.io, and all the email seems to be arriving properly.
Mail sending classes
sealed class NullMailSender : IMailSender
{
public void Send(IList<string> error, string from, string to, string title, string body)
{
}
}
sealed class SmtpMailSender : IMailSender
{
private const int NumberOfRetriesOnError = 3;
private const int DelayOnError = 1000;
public void Send(IList<string> errors, string from, string to, string title, string body)
{
for (int i = 0; i <= NumberOfRetriesOnError; ++i)
{
try
{
using (var x = new SmtpClient("smtp.mailtrap.io", 2525))
{
var smtp = new SmtpClient("smtp.mailtrap.io", 2525)
{
Credentials = new NetworkCredential("username", "password"),
EnableSsl = true
};
var mail = new MailMessage();
mail.From = new MailAddress(from);
mail.Subject = title;
mail.To.Add(to);
mail.Body = body;
smtp.Send(mail);
Console.WriteLine("Sending {0} mail to:{1}...", title, to);
}
break;
}
catch (SmtpException e)
{
if (i < NumberOfRetriesOnError)
Thread.Sleep((i + 1) * DelayOnError);
else
errors.Add($"{to}: {e.Message}");
}
}
}
}
}
The mail assembly, collecting and figuring out what is supposed to be in the email.
public class MailAssembly
{
public List<string> Errors { get; } = new List<string>();
public IEnumerable<Customer> Customers { get; set; }
public IEnumerable<Order> Orders { get; set; }
public IMailSender Sender { get; set; }
private const string OurEmailAddress = "[email protected]";
//Sends all new customers, with the title of the emails and the email template to the send function
public void SendWelcomeEmails()
{
Send(GetNewCustomers(), "Welcome", Resources.WelcomeEmailTemplate);
}
//Sends all customer without recent orders, with the title "Comeback" with the email temple to the send function
public void SendComeBackEmail()
{
Send(GetCustomersWithoutRecentOrders(), "ComeBack", Resources.ComeBackEmailTemplate);
}
// Returns all new customers in the "database" who are newer than 1 day
private IEnumerable<Customer> GetNewCustomers()
{
var yesterday = DateTime.UtcNow.Date.AddDays(-1);
return Customers.Where(x => x.CreatedDateTime >= yesterday);
}
// Returns all customers who haven't made an order in 1 month
private IEnumerable<Customer> GetCustomersWithoutRecentOrders()
{
var oneMonthAgo = DateTime.UtcNow.Date.AddMonths(-1);
return Customers.Where(c =>
{
var latestOrder = Orders
.Where(o => o.CustomerEmail == c.Email)
.OrderByDescending(o => o.OrderDatetime)
.FirstOrDefault();
return latestOrder != null
&& latestOrder.OrderDatetime < oneMonthAgo;
});
}
// The Handlebar reads the email temlate, then sends all the email data to the interface send function
private void Send(IEnumerable<Customer> customers, string title, string template)
{
if (Sender == null)
throw new InvalidOperationException("Cannot send...");
Errors.Clear();
foreach (var customer in customers)
{
var compiledTemplate =
HandlebarsDotNet.Handlebars.Compile(template);
var body = compiledTemplate(new { CompanyName = "Company", FullName = customer.FullName, valueOfVoucher = "50", voucher = "{voucherAcessCode}" });
Sender.Send(Errors, OurEmailAddress, customer.Email, title, body);
}
}
}
}
Interface part
public interface IMailSender
{
void Send(IList<string> error, string from, string to, string title, string body);
}
Main method, creating an instance of the classes
static void Main(string[] args)
{
var sender = new MailAssembly
{
Customers = DataLayer.ListCustomers(),
Orders = DataLayer.ListOrders(),
Sender = new SmtpMailSender()
};
sender.SendComeBackEmail();
sender.SendWelcomeEmails();
if (sender.Errors.Any()) Console.WriteLine(String.Join("\n", sender.Errors));
else
Console.WriteLine("All emails should have been sent succesfully");
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
And finally, the data layer
public class Customer
{
public string FullName { get; set; }
public string Email { get; set; }
public DateTime CreatedDateTime { get; set; }
}
public class Order
{
public string CustomerEmail { get; set; }
public DateTime OrderDatetime { get; set; }
}
class DataLayer
{
/// <summary>
/// Mockup method for all customers
/// </summary>
public static List<Customer> ListCustomers()
{
return new List<Customer>()
{
new Customer(){Email = "[email protected]", FullName = "Jane Porter", CreatedDateTime = DateTime.UtcNow.AddHours(-7)},
new Customer(){Email = "[email protected]", FullName = "Jeremy Longbottom",CreatedDateTime = DateTime.UtcNow.AddDays(-1)},
new Customer(){Email = "[email protected]", FullName = "Jock Ginger",CreatedDateTime = DateTime.UtcNow.AddMonths(-6)},
new Customer(){Email = "[email protected]", FullName = "Jeryll Potter",CreatedDateTime = DateTime.UtcNow.AddMonths(-1)},
new Customer(){Email = "[email protected]", FullName = "Jack Jacksson",CreatedDateTime = DateTime.UtcNow.AddMonths(-2)},
new Customer(){Email = "[email protected]", FullName = "Dope Jake",CreatedDateTime = DateTime.UtcNow.AddDays(-5)}
};
}
/// <summary>
/// Mockup method for listing all orders
/// </summary>
public static List<Order> ListOrders()
{
return new List<Order>()
{
new Order(){CustomerEmail = "[email protected]", OrderDatetime = DateTime.UtcNow.AddMonths(-6)},
new Order(){CustomerEmail = "[email protected]", OrderDatetime = DateTime.UtcNow.AddMonths(-2)},
new Order(){CustomerEmail = "[email protected]", OrderDatetime = DateTime.UtcNow.AddDays(-2)}
};
}
SmtpMailSender.Send()
you should not have bothx
andsmtp
, merge them... \$\endgroup\$