New Mail.dll tutorial video

New Mail.dll tutorial video is out. You can watch it here:

Mail.dll tutorial video.

ISession.Load returns object with zero ID


Recently we had a following problem with NHibernate, and although I love NHiberante, it does not always behave as expected.

We have a Person class correctly mapped in NHibernate:

public class Person
{
	public int Id { get; set; }
	public int Name { get; set; }
}

We saw that sometimes we were getting a Person with Id equal to zero, from our repository:

public class PersonRepository
{
	private ISession _session;

	//...

	public Person LoadById(int id)
	{
		return _session.Load<person>(id);
	}
}

We narrowed the problem down and wrote this little test:

[Test]
public void LoadById_Loads_IdIsSet()
{
    _context.ExecuteInTransaction(() =>
		{
			Person person = _context.PersonRepository.LoadById(7);
			Assert.AreEqual(7, person.Id);
		}
	);
}

…which of course failed.

After initial surprise, we took a second look at the Person class and we saw that we are missing virtual keyword on properties. NHibernate is not able to create a correct Proxy object.

public class Person
{
	public virtual int Id { get; set; }
	public virtual int Name { get; set; }
}

This fixed the issue.
Strange thing is that we expect that NHibernate would throw an exception is such case.

Source lines of code count using PowerShell

Source lines of code (SLOC) is a software metric used to measure the size of a software program by counting the number of lines in the text of the program’s source code.

As we all know the disadvantages of this metric, sometimes we simply want to know.

Here’s a PowerShell script, that recursively searches for *.cs files and counts the lines (empty lines and comments are excluded)

(dir -include *.cs -recurse | select-string "^(\s*)//" -notMatch | select-string "^(\s*)$" -notMatch).Count

Brief description of what all parts are doing:

  • dir -include *.cs -recurse : Lists all *.cs files, you can add additional extensions using a comma.
  • select-string “^(\s*)//” -notMatch : Exclude comments.
  • select-string “^(\s*)$” -notMatch : Exclude empty lines.

Localized Gmail IMAP folders


There are no well-know names for common folders such as Drafts, Trash, Spam, … on IMAP servers.

The problem is even worse when you use localized version of IMAP client. Gmail folder names are localized with respect to the user localization settings, so ‘[Gmail]/All Mail’ show as ‘[Gmail]/Todos’ to Spanish users for example.

Google and Apple developed a special IMAP XLIST command to address this issue.

IMAP XLIST command returns a list of folders and their well-know flags.

Here’s the sample XLIST response:


C: A001 CAPABILITY
S: * CAPABILITY IMAP4rev1 ID XLIST ...
S: A001 OK Thats all she wrote! 17if1168678ebj.35
C: A002 XLIST "" "*"
S: * XLIST (\HasNoChildren \Inbox) "/" "Inbox"
S: * XLIST (\HasNoChildren \AllMail) "/" "[Gmail]/All Mail"
S: * XLIST (\HasNoChildren \Drafts) "/" "[Gmail]/Drafts"
S: * XLIST (\HasNoChildren \Spam) "/" "[Gmail]/Spam"
...

As you can see XLIST is advertised in CAPABILITY response.
You can probably spot additional flags in the XLIST response: \AllMail, \Spam, \Drafts…

Mail.dll IMAP library supports XLIST command (and SPECIAL-USE extension). It is used automatically when server advertises support for this feature.

You can use CommonFolders class to match folder names with they real purpose.

Take a look at the examples:

// C# version:

using (Imap imap = new Imap())
{
    imap.ConnectSSL("imap.gmail.com");
    imap.UseBestLogin("pat@gmail.com", "app-password");

    CommonFolders folders = new CommonFolders(imap.GetFolders());

    Console.WriteLine("Inbox folder: " + folders.Inbox.Name);
    Console.WriteLine("Sent folder: " + folders.Sent.Name);

    // You can select folders easy:

    imap.Select(folders.Inbox);
    imap.Select(folders.Sent);

    imap.Close();
}
' VB.NET version:

Using imap As New Imap()
    imap.ConnectSSL("imap.gmail.com")
    imap.UseBestLogin("pat@gmail", "app-password")

    Dim folders As New CommonFolders(imap.GetFolders())

    Console.WriteLine("Inbox folder: " + folders.Inbox.Name)
    Console.WriteLine("Sent folder: " + folders.Sent.Name)

    ' You can select folders easy:

    imap.Select(folders.Inbox)
    imap.Select(folders.Sent)

    imap.Close()
End Using

Remember to enable IMAP in Gmail (your authentication options are app-passwords and OAuth 2.0).

Email template engine

Newest version of Mail.dll email library for .NET includes easy to use template engine.

It allows you to easily create html template for your emails:

Loading and rendering such template requires one line of code:

// C# version

Contact templateData = ...;

 string html = Template
     .FromFile("template.txt")
     .DataFrom(templateData )
     .Render();
' VB.NET version

Dim templateData As Contact = ....

Dim html As String = Template _
     .FromFile("template.txt") _
     .DataFrom(templateData) _
     .Render()

This is how the template looks like:

<html>
<head>
    <title>Your order</title>
</head>
<body>
	Hi [FirstName] [LastName],
	<br />
	<p>
	Your account [if Verified]has[else]<strong>has not</strong>[end] been verified. <br/>
	Your password is: [Password].
	</p>
	<p>
	Here are your orders:
	</p>
	[foreach Orders]
		<p>
		Order sent to <strong>[Street]</strong>:
		</p>
		<table style="width: 30%;">
			[foreach Items]
				<tr style="background-color: #E0ECFF;">
					<td>[Name]</td><td>[Price]</td>
				</tr>
			[end]
		</table>
	[end]
	<p>
		Thank you for your orders.
	<p>
</body>
</html>

Here’s the sample code that loads, fills the template and sends it using Mail.dll:

// C# version

string rendered = Template
    .FromFile("template.txt")
    .DataFrom(templateData)
    .Render();

MailBuilder builder = new MailBuilder();
builder.Html = rendered;
builder.Subject = "Your order";
builder.From.Add(new MailBox("alice@mail.com", "Alice"));
builder.To.Add(new MailBox("bob@mail.com", "Bob"));
IMail email = builder.Create();

using (Smtp client = new Smtp())
{
    client.ConnectSSL("smtp.example.org");
    client.UseBestLogin("alice@example.org", "password");
    client.SendMessage(email);
    client.Close();
}
// C# fluent interface version

using Fluent = Limilabs.Mail.Fluent;

Fluent.Mail.Html(Template
              .FromFile("template.txt")
              .DataFrom(templateData)
              .Render())
    .From(new MailBox("alice@mail.com", "Alice"))
    .To(new MailBox("bob@mail.com", "Bob"))
    .Subject("Your order")
    .UsingNewSmtp()
    .WithCredentials("alice@example.org", "password")
    .Server("smtp.example.org")
    .WithSSL()
    .Send();
' VB.NET version

    Dim rendered As String = Template _
        .FromFile("template.txt") _
        .DataFrom(templateData) _
        .Render()

    Dim builder As MailBuilder = New MailBuilder()
    builder.Html = rendered
    builder.Subject = "Your order"
    builder.From.Add(New MailBox("alice@mail.com", "Alice"))
    builder.[To].Add(New MailBox("bob@mail.com", "Bob"))
    Dim email As IMail = builder.Create()

    Using client As Smtp = New Smtp()
        client.ConnectSSL("smtp.example.org")
        client.UseBestLogin("alice@example.org", "password")
        client.SendMessage(email)
        client.Close()
    End Using
' VB.NET fluent interface version

Imports Fluent = Limilabs.Mail.Fluent;

Fluent.Mail.Html(Template _
               .FromFile("template.txt") _
               .DataFrom(templateData) _
               .Render()) _
     .From(New MailBox("alice@mail.com", "Alice")) _
     .[To](New MailBox("bob@mail.com", "Bob")) _
     .Subject("Your order") _
     .UsingNewSmtp() _
     .WithCredentials("alice@example.org", "password") _
     .Server("smtp.example.org") _
     .WithSSL() _
     .Send()

You can also specify text version of the message, by using Text(string) method. In most cases this is unnecessary, as Mail.dll automatically extracts plain text from html and adds the plain text version to the email message.

And this is how the data (templateData variable), used by the template, look like:

// C# version

public class Contact
{
    public List<order> Orders { get; private set; }

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public bool Verified;
    private string Password { get; set; }

    public Contact()
    {
        Orders = new List<order>();
    }
} ;
' VB.NET version

Public Class Contact
	Public Property Orders() As List(Of Order)
		Get
			Return m_Orders
		End Get
		Private Set
			m_Orders = Value
		End Set
	End Property
	Private m_Orders As List(Of Order)

	Public Property FirstName() As String
		Get
			Return m_FirstName
		End Get
		Set
			m_FirstName = Value
		End Set
	End Property
	Private m_FirstName As String
	Public Property LastName() As String
		Get
			Return m_LastName
		End Get
		Set
			m_LastName = Value
		End Set
	End Property
	Private m_LastName As String
	Public Verified As Boolean
	Private Property Password() As String
		Get
			Return m_Password
		End Get
		Set
			m_Password = Value
		End Set
	End Property
	Private m_Password As String

	Public Sub New()
		Orders = New List(Of Order)()
	End Sub
End Class