Sending email with embedded image

There are 3 ways of embedding images inside email message:

  • Embedding image using multipart/related MIME object
  • Referencing remote web server (requires image maintenance for a long time)
  • Using BASE64 encoded inline images (not supported by many email clients)

In this article we’ll show how to use the first method. This method makes message bigger, but images don’t need to be stored on your web server. Furthermore this method is widely supported by email clients.

How this works

When MIME tree is build we specify that images added are in relation to HTML content of the message. We also specify content disposition as inline. This means that those images are not actual attachments, but rather should be displayed to user when he/she opens the message.

In Mail.dll it is quite simple as we just need to add the image to MailBuilder.Visualscollection using AddVisual method. Remember that you need to set the image’s Content-ID, as it is required in the next step.

HTML body of the message references those images by using special "cid:" protocol. It specifies Content-ID of the image that should be used:

This is our <strong>brand new</strong> logo: <br />
<img src="cid:logo@example.com" />

Create HTML message using MailBuilder

// Use builder class to create an email
MailBuilder builder = new MailBuilder();

// Set From, To
builder.From.Add(new MailBox("alice@mail.com", "Alice"));
builder.To.Add(new MailBox("bob@mail.com", "Bob"));
builder.Subject = "Test";

// Set HTML content (Notice the src="cid:..." attribute)
builder.Html = @"<html><body><img src=""cid:logo@example.com"" /></body></html>";

// Html automatically extracts plaint text from html so,
// unless you want to specify plain text explicitly, you don't need to use this line:
builder.Text = "This is text version of the message.";

// Add image and set its Content-Id
MimeData visual = builder.AddVisual(@"c:\image.jpg");
visual.ContentId = "logo@example.com";

IMail email = builder.Create();

And of course the VB.NET version of the same code:

' Use builder class to create an email
Dim builder As New MailBuilder()

' Set From, To
builder.From.Add(New MailBox("alice@mail.com", "Alice"))
builder.To.Add(New MailBox("bob@mail.com", "Bob"))
builder.Subject = "Test"

' Set HTML content (Notice the src="cid:..." attribute)
builder.Html = "<html><body><img src=""cid:logo@example.com"" /></body></html>"

' Html automatically extracts plaint text from html so,
' unless you want to specify plain text explicitly, you don't need to use this line:
builder.Text = "This is text version of the message."

' Add image and set its Content-Id
Dim visual As MimeData = builder.AddVisual("c:\image.jpg")
visual.ContentId = "logo@example.com"

Dim email As IMail = builder.Create()

HTML message using fluent interface

// C#

using Fluent = Limilabs.Mail.Fluent;

IMail email = Fluent.Mail.Html(@"<html><body><img src=""cid:logo@example.com"" /></body></html>")
        .From(new MailBox("alice@mail.com", "Alice"))
        .To(new MailBox("bob@mail.com", "Bob"))
        .Subject("Test")
        .AddVisual(@"c:\image.jpg")
        .SetContentId("logo@example.com")
        .Create();

And of course the VB.NET version using fluent interface:

' VB.NET

IMail email = Mail.Html("<html><body><img src=""cid:logo@example.com"" /></body></html>") _
	.From(New MailBox("alice@mail.com", "Alice")) _
	.To(New MailBox("bob@mail.com", "Bob")) _
	.Subject("Test").AddVisual("c:\image.jpg") _
	.SetContentId("logo@example.com") _
	.Create()

Sending email using SMTP

Now we can connect to SMTP server and send the HTML email with embedded image we recently created:

using (Smtp client = new Smtp())
{
    client.Connect("smtp.example.com"); // or ConnectSSL
    client.UseBestLogin("user", "password");
    client.SendMessage(email);
    client.Close();
}

You can download Mail.dll email client here.

NHibernate ignores SetMaxResults in SQL

nhibernateRecently me and my team realized that several our queries, that include maximum limit on the recordset, are limited in the web application and not on the Oracle side.

First thought was that somebody simply forgot to add an invocation of SetMaxResults method, but our repository code was very obvious about it:

IQuery query = session.CreateQuery(hqlQuery);
query.SetMaxResults(100);

After some investigation it turned out that NHiberante is not able to use ROWNUM on the SQL side, when there is a fetch join on the collection.

EmployeeDatas

This is HQL query we use:

select
    user
from
    User user
    /* next line causes no rownum check */
    inner join fetch user.EmployeeDatas EmployeeData
    left join fetch EmployeeData.Country country

And some NHibernate code:

public IList List(ISessionImplementor session, QueryParameters queryParameters)
{
	// ...
	if ( hasLimit && ContainsCollectionFetches )
	{
		log.Warn( "firstResult/maxResults specified with collection fetch; applying in memory!" );

NHibernate simply ignores the limit when executing the query and applies it on the result.

At some point I thought that it is not possible to even create such SQL query, but I was proven to be wrong. The query would look similar to this proof-of-concept:

SELECT
*
FROM
(
SELECT
*
FROM
TUSER u
WHERE
/* Add conditions here: */
EXISTS(…)
/* Here is DB-side limit: */
AND ROWNUM <= 14 ) AS u /* And now fetch all collections needed */ INNER JOIN TEMPLOYEEDATA d ON u.ID = d.USERID [/sql] So it's clear that it is possible to improve this in NHibernate. In our case change was simple: We changed one-to-many mapping to one-to-one.

But if you are not able to do this because of your requirements I would use custom SQL view and mapped it in NHibernate. The second idea would be to use NHibernate detached criteria and sub-queries.

Finally the unit test that we used to check if the limit is done on SQL database side:

[Test]
public void FindUsersBy_QueryWithLimit_LimitsOnSQLSide()
{
    using (LogChecker logChecker = new LogChecker("NHibernate", Level.Warn))
    {
        IList<user> users = this._context.ExecuteInTransaction(() =>
        {
            UserQuery q = new UserQuery();
            q.UserId = "ILMT";
            q.MaxNumberOfRecords = 14;
            return this._context.UserRepository.FindUsersBy(q);
        });
        Assert.IsEmpty(logChecker.Messages);
        Assert.AreEqual(14, users.Count);
    }
}

Here you can see the implementation of the LogChecker log4net log reader

Regex bug on Mono

monoThis is a good advice for all creating applications using .NET that need to work on both Microsoft .NET and Mono.

It seems that Mono ignores group indexes specified explicitly.
Here’s the code to reproduce this problem.

The results on Mono are incorrect.

Regex parser = new Regex("(?<2>.*?),(?<1>.*?),");
Match match = parser.Match("first,second,");
Console.WriteLine("Groups[1].Value={0}", match.Groups[1].Value);
Console.WriteLine("Groups[2].Value={0}", match.Groups[2].Value);

MS implementation:

C:\1\ConsoleApplication\1\bin\Debug>ConsoleApplication1.exe
Groups[1].Value=second
Groups[2].Value=first

Mono 2.4.2.3 implementation:

C:\1\ConsoleApplication\1\bin\Debug>mono.exe ConsoleApplication1.exe
Groups[1].Value=first
Groups[2].Value=second

The workaround is simple use names instead of numbers:

Regex parser = new Regex("(?<first>.*?),(?<second>.*?),");
Match match = parser.Match("first,second,");
Console.WriteLine("Groups[first].Value={0}", match.Groups["first"].Value);
Console.WriteLine("Groups[second].Value={0}", match.Groups["second"].Value);

Finder for any IEnumerable

NHibernate requires you to use IList<T> properties a lot:

public IList<process> Processes { get; set; }

Searching through them is a real pain especially when you live in .NET 2.0 world:

public List<processModel> GetDeleted()
{
    List<process> list= new List<process>();
    foreach (Process process in this.Processes)
    {
        if (process.IsDeleted)
            list.Add(process);
    }
    return list;
}

If you can’t move your project to .NET 3.5 and use extension methods here’s the solution for you.

This is the simple class for making searches on any IEnumerable<T> easier:

public List<processModel> GetDeleted()
{
        return Finder.Create(this.Processes)
                .FindAll(x => x.IsDeleted);
}

Nice, isn’t it?

The Finder class itself:

public class Finder
{
    public static Finder<t> Create<t>(IEnumerable<t> list)
    {
        return new Finder<t>(list);
    }
};

public class Finder<t>
{
    private readonly IEnumerable<t> _list;

    public Finder(IEnumerable<t> list)
    {
        _list = list;
    }

    public T Find(Predicate<t> func)
    {
        foreach (T element in _list)
        {
            if (func(element))
                return element;
        }
        return default(T);
    }

    public List<t> FindAll(Predicate<t> func)
    {
        List<t> list = new List<t>();
        foreach (T element in _list)
        {
            if (func(element))
                list.Add(element);
        }
        return list;
    }

    public ReadOnlyCollection<t> FindAllAsReadOnly(Predicate<t> func)
    {
        return new ReadOnlyCollection<t>(this.FindAll(func));
    }

    public bool Contains(Predicate<t> func)
    {
        foreach (T element in _list)
        {
            if (func(element))
                return true;
        }
        return false;
    }
};

and some tests:

[TestFixture]
public class FinderTest
{
    [Test]
    public void FindAll_ForValueType()
    {
        List<int> list = new List<int> { 1, 2, 10, 12 };
        Finder<int> finder = Finder.Create(list);
        Assert.AreEqual(1, finder.FindAll(x => x == 1).Count);
    }

    [Test]
    public void FindAll_ForRefType()
    {
        List<box> list = new List<box> { new Box(1), new Box(1), new Box(15) };
        Finder<box> finder = Finder.Create(list);
        Assert.AreEqual(2, finder.FindAll(x => x.Id == 1).Count);
    }

    [Test]
    public void FindAllAsReadOnly_Find_ForRefType()
    {
        List<box> list = new List<box> { new Box(1), new Box(1), new Box(15) };
        Finder<box> finder = Finder.Create(list);
        ReadOnlyCollection<box> only = finder.FindAllAsReadOnly(x => x.Id == 1);
        Assert.AreEqual(2, only.Count);
    }

    [Test]
    public void Find_ForValueType()
    {
        List<int> list = new List<int> { 1 ,2, 10, 12 };
        Finder<int> finder = Finder.Create(list);
        Assert.AreEqual(1, finder.Find(x => x == 1));
        Assert.AreEqual(12, finder.Find(x => x == 12));
        Assert.AreEqual(0, finder.Find(x => x == 999));
    }

    [Test]
    public void Find_ForRefType()
    {
        List<box> list = new List<box> { new Box(21),  new Box(2),  new Box(15) };
        Finder<box> finder = Finder.Create(list);
        Assert.AreEqual(21, finder.Find(x => x.Id == 21).Id);
        Assert.AreEqual(2, finder.Find(x => x.Id == 2).Id);
        Assert.AreEqual(null, finder.Find(x => x.Id == 999));
    }

    [Test]
    public void Find_ForEmptyList()
    {
        Finder<box> finder = Finder.Create(new List<box>());
        Assert.IsNull(finder.Find(x => x.Id == 21));
        Assert.IsNull(finder.Find(x => x.Id == 2));
        Assert.IsNull(finder.Find(x => x.Id == 999));
    }

    internal class Box
    {
        readonly int _id;

        public int Id
        {
            get { return _id; }
        }

        public Box(int id)
        {
            _id = id;
        }
    }
} ;

Many-to-one with non unique columns

6387611d

Tables look as follows:

User
+ID
+PERSON_ID
+PRIMARY_USER

DeputyRole
+PERSON_ID

Disclaimer: Yes I know the db schema is broken, but we can not do anything about it.

Why didn’t we use standard propery-ref and column mapping but formula instead?

  1. USER.PERSON_ID is not unique in USER table.
  2. USER.PERSON_ID AND USER.PRIMARY_USER = ‘Y’ is unique in USER table.
  3. We are using formula to get single record from USER (ID) with specified PERSON_ID and PRIMARY_USER = ‘Y’

In DeputyRole.hbm.xml we have:

<many-to-one name="User"
        class="DomainModel.Users.Entities.User"
        lazy="false"
        formula = "(SELECT u.ID FROM USER u WHERE
                                u.PRIMARYUSERID = 'Y'
                                AND u.PERSON_ID = PERSON_ID)"
/>

Can this be done in better way?