HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

SMTP client to send search results

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
searchsmtpclientresultssend

Problem

I have the following working code to send search results via e-mail. Since we have segregated domains that cannot talk to each other, the program needs to support multiple SMTP servers.

```
private static int[] _SMTPport = new int[2] { 25, 587 };
private static string[] _SMTPserver = new string[4]
{
"smtp.domain1.local",
"smtp.domain2.local",
"smtp.domain3.local",
"smtp.domain4.local"
};

switch (sUserDomain)
{
case: "domain1":
case: "DOMAIN1":
Send_Email(_SMTPserver[0], _SMTPport[0], false);
break;
case: "domain2":
case: "DOMAIN2":
Send_Email(_SMTPserver[0], _SMTPport[0], false);
break;
case: "domain3":
case: "DOMAIN3":
Send_Email(_SMTPserver[0], _SMTPport[0], false);
break;
case: "domain4":
case: "DOMAIN4":
Send_Email(_SMTPserver[0], _SMTPport[0], false);
break;
default:
Send_Email(_SMTPserver[3], _SMTPport[0], false);
break;
}

private static void Send_Email(string _server, int _port, bool _ssl)
{
StringBuilder emailString = new StringBuilder();
MailMessage mMessage = new MailMessage();

SmtpClient smtpClient = new SmtpClient(_server);
smtpClient.Port = _port;
smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;

if (_ssl.Equals(true))
{ smtpClient.EnableSsl = true; }

ServicePointManager.ServerCertificateValidationCallback = delegate(object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{ return true; };

mMessage.From = new MailAddress("from-address");
mMessage.To.Add(sEmailAddress);

mMessage.Subject = "Logs from " + Environment.GetEnvironmentVariable("COMPUTERNAME") + " searched " + sDate + " for " + sSearchTerm;
emailString.Append("Here are the file(s) on " + Environment.GetEnvironmentVariable("COMPUTERNAME") + " under " + sDate + " that contain " + sSearchTerm + "");
emailString.AppendLine("Filenames");
int count = 0;
foreach (var item in logList)
{
if (count % 2 == 0)
{
emailString.AppendLine("" + Path

Solution

SRP - single responsibility principle

The Send_Email() method is responsible for to many things.

The method is

  • creating and configuring a SmtpClient



  • creating and configuring a MailMessage



  • composing the body of the MailMessage



This should be extracted to separate methods.

So let us refactor this method.

First we write a method to create a SmtpClient.

private SmtpClient GetSmtpClient(string serverAddress, int serverPort, bool enableSsl)
{
    SmtpClient smtpClient = new SmtpClient(serverAddress);
    smtpClient.Port = serverPort;
    smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
    smtpClient.EnableSsl = enableSsl;

    return smtpClient;
}


This had been pretty easy, hadn't it ? So, next we add a method composing the Subject by using the String.Format() method.

private String ComposeSubject(LogResult logResult) 
{
    return String.Format("Logs from {0} searched {1} for {2}", logResult.ComputerName, logResult.SearchDate, logResult.SearchTerm);
}


But wait, what is LogResult ? That is a class we need to create, so let us do it. As we see it should have at least the properties ComputerName, SearchDate and SearchTerm. We add also a ReadOnlyCollection FileNames property.

public class LogResult
{
    public String ComputerName { get; private set; }
    public DateTime SearchDate { get; private set; }
    public String SearchTerm { get; private set; }
    public ReadOnlyCollection FileNames { get; private set; }

    public LogResult(String computerName, DateTime searchDate, String searchTerm, List logList)
    {
        ComputerName = computerName;
        SearchDate = searchDate;
        SearchTerm = searchTerm;
        FileNames = ToFileNames(logList);
    }

    public LogResult(DateTime searchDate, String searchTerm, List logList)
        : this(Environment.GetEnvironmentVariable("COMPUTERNAME"), searchDate, searchTerm, logList)
    { }

    private ReadOnlyCollection ToFileNames(List logList)
    {
        IList fileNames = new List(logList.Count);
        foreach (String completeFileName in logList)
        {
            fileNames.Add(Path.GetFileName(completeFileName));
        }
        return new ReadOnlyCollection(fileNames);
    }
}


So now we will use this class to also compose the email body. Here we are using the feature of the StringBuilder that it is returning a StringBuilder for calling the AppendXX() methods. So we can call StringBuilder.Append().Append()...we are using also the StringBuilder.AppendFormat() method.

private String ComposeMailBody(LogResult logResult)
{
    StringBuilder bodyBuilder = new StringBuilder(1024);

    bodyBuilder.Append("")
        .AppendFormat("Here are the file(s) on  {0}", logResult.ComputerName)
        .AppendFormat(" under {0}", logResult.SearchDate)
        .AppendFormat(" that contain {0}",logResult.SearchTerm)
        .AppendLine("Filenames");

    int count = 0;
    foreach (String fileName in logResult.FileNames)
    {
        if (count % 2 == 0)
        {
            bodyBuilder.AppendFormat("{0}",fileName);
        }
        else
        {
            bodyBuilder.AppendFormat("{0}",fileName);
        }
        count++;
    }
    bodyBuilder.Append("");

    return bodyBuilder.ToString();
}


The last method we introduce is for creating a MailMessage.

private MailMessage CreateMessage(LogResult logResult, String receiver)
{
    MailMessage message = new MailMessage("from-address", receiver);
    message.Subject = ComposeSubkject(logResult);
    message.Body = ComposeMailBody(logResult);

    return message;
}


Now the former Send_Email() method would look like

private static void SendEmail(string server, int port, bool ssl)
{
    LogResult logResult = new LogResult(sDate ,sSearchTerm, logList);
    SmtpClient smtpClient = GetSmtpClient(server, port, ssl);
    MailMessage message = CreateMailMessage(sEmailAddress);

    smtpClient.Send(message);
}


EmailClass

Using auto properties will reduce your code a lot. Auto properties are properties with a hidden backing field generated by the compiler.

public class EmailClass
{
    public string Domain { get; set; }

    public string EmailAddress { get; set; }

    public string FromAddress { get; set; }

    public string Subject { get; set; }

    public List Message { get; set; }

}


Naming

Based on the naming guidlines method names should be named using PascalCase casing. So Send_Email should be renamed to SendEmail.

Based on the same guidlines method parameters should be named using camelCase casing. the signature of the former Send_Email() method should look like

private static void SendEmail(string server, int port, bool ssl)

Code Snippets

private SmtpClient GetSmtpClient(string serverAddress, int serverPort, bool enableSsl)
{
    SmtpClient smtpClient = new SmtpClient(serverAddress);
    smtpClient.Port = serverPort;
    smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
    smtpClient.EnableSsl = enableSsl;

    return smtpClient;
}
private String ComposeSubject(LogResult logResult) 
{
    return String.Format("Logs from {0} searched {1} for {2}", logResult.ComputerName, logResult.SearchDate, logResult.SearchTerm);
}
public class LogResult
{
    public String ComputerName { get; private set; }
    public DateTime SearchDate { get; private set; }
    public String SearchTerm { get; private set; }
    public ReadOnlyCollection<String> FileNames { get; private set; }

    public LogResult(String computerName, DateTime searchDate, String searchTerm, List<String> logList)
    {
        ComputerName = computerName;
        SearchDate = searchDate;
        SearchTerm = searchTerm;
        FileNames = ToFileNames(logList);
    }

    public LogResult(DateTime searchDate, String searchTerm, List<String> logList)
        : this(Environment.GetEnvironmentVariable("COMPUTERNAME"), searchDate, searchTerm, logList)
    { }

    private ReadOnlyCollection<String> ToFileNames(List<String> logList)
    {
        IList<String> fileNames = new List<String>(logList.Count);
        foreach (String completeFileName in logList)
        {
            fileNames.Add(Path.GetFileName(completeFileName));
        }
        return new ReadOnlyCollection<String>(fileNames);
    }
}
private String ComposeMailBody(LogResult logResult)
{
    StringBuilder bodyBuilder = new StringBuilder(1024);

    bodyBuilder.Append("<p style='font-family:arial,helvetica,sans-serif;'>")
        .AppendFormat("Here are the file(s) on <b> {0}", logResult.ComputerName)
        .AppendFormat("</b> under <b>{0}", logResult.SearchDate)
        .AppendFormat("</b> that contain <b><i>{0}</i><b></p><br/><br/>",logResult.SearchTerm)
        .AppendLine("<table width='100%' border='0' align='center' cellpadding='5' cellspacing='0' style='font-family:arial,helvetica,sans-serif;'><tbody><tr><td style='padding:5px;background-color:rgb(169, 169, 169);color:white;'>Filenames</td></tr>");

    int count = 0;
    foreach (String fileName in logResult.FileNames)
    {
        if (count % 2 == 0)
        {
            bodyBuilder.AppendFormat("<tr><td style='background-color:#bada55;border-collapse:collapse;'>{0}</td></tr>",fileName);
        }
        else
        {
            bodyBuilder.AppendFormat("<tr><td style='background-color:#55bada;border-collapse:collapse;'>{0}</td></tr>",fileName);
        }
        count++;
    }
    bodyBuilder.Append("</tr></tbody></table>");

    return bodyBuilder.ToString();
}
private MailMessage CreateMessage(LogResult logResult, String receiver)
{
    MailMessage message = new MailMessage("from-address", receiver);
    message.Subject = ComposeSubkject(logResult);
    message.Body = ComposeMailBody(logResult);

    return message;
}

Context

StackExchange Code Review Q#69004, answer score: 5

Revisions (0)

No revisions yet.