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