We can use port 25, 465 or 587 to send emails. Azure VM port 25 is blocked by Microsoft and can only be unblocked by a request.
Using email template to compose and send emails
This is a simple prototype to compose and send HTML emails using a sample template.
Here is the template;
String emailTemplate = @"
<html lang=""en"">
<head>
<meta content=""text/html; charset=utf-8"" http-equiv=""Content-Type"">
<title>
{0}
</title>
<style type=""text/css"">
HTML{background-color: #e8e8e8;}
.document-table{font-size: 12px; padding: 3px; border-collapse: collapse; border-spacing: 0;}
.document-table .description{color: #505050;}
.document-table td{border: 1px solid #D1D1D1; background-color: #F3F3F3; padding: 0 10px;}
.document-table th{border: 1px solid #424242; color: #FFFFFF;text-align: left; padding: 0 10px;}
.green{background-color: #6B9852;}
.wrapper { margin-bottom: 20px;}
</style>
</head>
<body>
<div class=""wrapper"">
Team,
</div>
<div class=""wrapper"">
SampleDb data pull job {1} last night. The job is configured to run on {2} basis.
</div>
<div class=""wrapper"">
SampleDb database is scanned between {3} for new documents. Following new document have been found and their data is copied over to the target database.
</div>
<div class=""wrapper"">
<table class=""document-table"">
<thead>
<tr>
<th class=""green"">AccountNumber</th>
<th class=""green"">FYQ</th>
<th class=""green"">DateAdded</th>
<th class=""green"">RowStatus</th>
</tr>
</thead>
<tbody>
{4}
</tbody>
</table>
</div>
<div class=""wrapper"">
<h5><br /><br />This inbox is unattended. please do not reply to this email. This is an auto-generated message.</h5>
</div>
</body>
</html>
";
We can use dynamic and ExpandoObject to create data. Alternatively you can grab data from database;
var list = new List<dynamic>();
dynamic row = new ExpandoObject();
row.AccountNumber = "XYZ-100";
row.FYQ = "Q1 FY 1999";
row.DateAdded = "1999-07-28 19:38:00.000";
row.RowStatus = "processed";
list.Add(row);
We need to fill template placeholders;
StringBuilder sb = new StringBuilder();
list.ForEach(x =>
{
sb.Append("<tr>");
sb.Append($"<td class='description'>{x.AwardNumber}</td>");
sb.Append($"<td>{x.FYQ}</td>");
sb.Append($"<td>{x.DateAdded}</td>");
sb.Append($"<td>{x.RowStatus}</td>");
sb.Append("</tr>");
});
replace all placeholders in HTML template;
string emailBody =
emailTemplate.Replace("{0}", "A demo email body title")
.Replace("{1}", "succeeded")
.Replace("{2}", "yearly")
.Replace("{3}", "01/01/1999 23.59.59 - 11/2/2021 02:00:00 AM")
.Replace("{4}", sb.ToString());
Compose and send email out;
MailMessage mailMessage = new MailMessage();
mailMessage.From = new MailAddress("FromAdam@mail.com", "Sample Data Pull");
mailMessage.To.Add("ToJoe@mail.com");
mailMessage.Body = emailTemplate;
mailMessage.Subject = "Test email";
mailMessage.IsBodyHtml = true;
mailMessage.SubjectEncoding = Encoding.UTF8;
mailMessage.BodyEncoding = Encoding.UTF8;
//send email
using (var client = new SmtpClient("smtp-host-address"))
{
//if true, user/pwd required, false network credentials will be used
client.UseDefaultCredentials = true;
if (client.UseDefaultCredentials)
{
client.Credentials = new NetworkCredential("FromAdmam@mail.com", "AdamSecretPassword");
}
client.EnableSsl = true;
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.Timeout = 3000; //3 seconds
client.Send(mailMessage);
}
Run your program and check your inbox. You will see HTML email with CSS style applied. The HTML table element is replace with our created data.
Have fun!
Dataset is empty, no tables in Dataset
This is how we can test if dataset is empty;
if(ds != null)
if(ds.Tables.Count > 0 )
if(ds.Tables[0].Rows.Count > 0)
We can loop through all tables in a method like this;
bool IsEmpty(DataSet dataSet)
{
foreach(DataTable table in dataSet.Tables)
if (table.Rows.Count != 0) return false;
return true;
}
Since a DataTable could contain deleted rows RowState = Deleted
, depending on what you want to achive, it could be a good idea to check the DefaultView
instead (which does not contain deleted rows).
bool IsEmpty(DataSet dataSet)
{
return !dataSet.Tables.Cast<DataTable>().Any(x => x.DefaultView.Count > 0);
}
Resource
https://stackoverflow.com/questions/2976473/how-to-test-if-a-dataset-is-empty
Dynamic type and Expando Object class
The dynamic
type indicates that use of the variable and references to its members bypass compile-time type checking. Instead, these operations are resolved at run time. The dynamic
type simplifies access to COM APIs such as the Office Automation APIs, to dynamic APIs such as IronPython libraries, and to the HTML Document Object Model (DOM).
Here is an example;
var list = new List<dynamic>();
dynamic row = new ExpandoObject();
row.AccountNumber = "XYZ-331";
row.FYQ = "Q3 FY 2021";
row.DateAdded = "2021-07-28 19:38:00.000";
row.RowStatus = "processed";
list.Add(row);
Here is the output;
list.ForEach(x =>
{
Console.WriteLine($"AccountNumber = { x.AccountNumber} \nFYQ = {x.FYQ}\nDateAdded = {x.DateAdded}\nRowStatus = {row.RowStatus}");
});
Resources
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/reference-types
https://docs.microsoft.com/en-us/dotnet/api/system.dynamic.expandoobject?view=net-5.0
https://www.codegrepper.com/code-examples/csharp/add+static+data+to+list+in+c%23
Appending two string builders
The most performant way of appending two string builders in .NET Framework 4, use this;
frontStringBuilder.Append(backStringBuilder);
For .NET Framework 2.0/3.5, use this;
frontStringBuilder.Append(backStringBuilder.ToString(0, backStringBuilder.Length));
This wouldn’t hurt performance in .NET FW 4.0.
Resources
https://stackoverflow.com/questions/6454392/how-to-append-two-stringbuilders