31

suppose i need to send mail to customer with customer detail and his order detail. i have template html data in a html file.customer data is there and as well as order detail is also there in same html template file. my html look like

<html>
<body>
Hi {FirstName} {LastName},

Here are your orders: 
{foreach Orders}
    Order ID {OrderID} Quantity : {Qty} <strong>{Price}</strong>. 
{end}

</body>
</html>

now i want to fill up all sample keyword surrounded with {} with actual value and also iterate and fill up orders.

i search google and found that microsoft provide a class called MailDefinition by which we can generate mail body dynamically. i got a sample code also like

MailDefinition md = new MailDefinition();
md.From = "test@domain.com";
md.IsBodyHtml = true;
md.Subject = "Test of MailDefinition";

ListDictionary replacements = new ListDictionary();
replacements.Add("<%Name%>", "Martin");
replacements.Add("<%Country%>", "Denmark");

string body = "
Hello <%Name%> You're from <%Country%>.";


MailMessage msg = md.CreateMailMessage("you@anywhere.com", replacements, body, new    System.Web.UI.Control());

by the above code we can replace pseudo value with actual value but i don't know how iterate in Orders detail and populate orders data.

so if it is possible using MailDefinition class then please guide me with code that how can i iterate in loop and generate body for orders detail.

Kiquenet
  • 14,494
  • 35
  • 148
  • 243
Thomas
  • 33,544
  • 126
  • 357
  • 626
  • If a 3rd Party library is an option, have a look at [dotLiquid](http://dotliquidmarkup.org/). You also may want to see my answer to a similar question: http://stackoverflow.com/a/28558160/79485 – Marcel Feb 25 '15 at 06:45
  • How can I download ***MailDefinition*** ? – Kiquenet Oct 07 '15 at 07:40
  • MailDefinition is part of .NET, in the System.Web namespace. You don't need to download it. – Alan B Oct 29 '15 at 15:08

2 Answers2

50

As an alternative to MailDefinition, have a look at RazorEngine https://github.com/Antaris/RazorEngine.

RazorEngine is a simplified templating framework built around Microsoft's new Razor parsing engine, used in both ASP.NET MVC3 and Web Pages. RazorEngine provides a wrapper and additional services built around the parsing engine to allow the parsing technology to be used in other project types.

It lets you use razor templates outside of ASP.NET MVC and then write something like this (not tested):

string template =
@"<html>
<body>
Hi @Model.FirstName @Model.LastName,

Here are your orders: 
@foreach(var order in Model.Orders) {
    Order ID @order.Id Quantity : @order.Qty <strong>@order.Price</strong>. 
}

</body>
</html>";

var model = new OrderModel {
    FirstName = "Martin",
    LastName = "Whatever",
    Orders = new [] {
        new Order { Id = 1, Qty = 5, Price = 29.99 },
        new Order { Id = 2, Qty = 1, Price = 9.99 }
    }
};

string mailBody = Razor.Parse(template, model);
Chuck Conway
  • 16,287
  • 11
  • 58
  • 101
David Tischler
  • 2,642
  • 22
  • 13
  • thanks for ur help but i am using asp.net webform not mvc. i want the code in such a way which will work also in windows application by c#. – Thomas May 09 '12 at 10:17
  • 1
    Really nice, simple solution! This was the best of the 10 or so I reviewed. I extended this a bit to create actual csthml template files. What's great is even though their in a simple class library, Visual Studio still picks up them up as cshtml and I get full Razor syntax support! – Tim Jul 29 '13 at 23:50
  • 1
    I like this. But my templating authors got used to KnockOut templates. Is there any Knockout server side API for templating? – Ravi Nov 05 '14 at 22:49
  • 1
    I found an exception because of bad razor syntax at foreach cycle. To solve that you need to add blocks where we have text (obviosly) inside the cycle. So this example should look like this: `@foreach(var order in Model.Orders) { Order ID @order.Id Quantity: @order.Qty @order.Price. }` – Pnctovski Dec 26 '14 at 13:45
1

You can't do such "complicated" logic with the default replacement stuff (the placeholder handling is made to be used for simple variables only, e.g. names or values).

You'll have to do the parsing yourself. Depending on the complexity (e.g. loops withing loops), this can get a bit tricky.

If you don't want or need such things, it's more trivial. E.g. use the regular expression \{foreach (.*?)\}(.*?)\{end\} to find such loops, then parse the contents/matched groups the way you need. Once that part is done, you could replace other values or use the default replacement feature.

Only downside with this approach is the fact that you'll have to recreate the mail for each recipient (i.e. you can't mass mail using MailDefinition).

Mario
  • 35,726
  • 5
  • 62
  • 78
  • i just do not understand how to handle loop. u said "If you don't want or need such things, it's more trivial. E.g. use the regular expression \{foreach (.*?)\}(.*?)\{end\} to find such loops, then parse the contents/matched groups the way you need. Once that part is done, you could replace other values or use the default replacement feature." can u plzz show me what u want me to do with small sample code. thanks – Thomas May 10 '12 at 07:03
  • A short introduction to regular expressions and how to use them to find patterns can be found here: http://support.microsoft.com/kb/308252/en-us If the regular expression matches, you'll know there is a loop. The capture groups will contain the object/group to iterate over as a string (in your example `"Orders"`) as well as the format string to be used for each order (`"Order ID {OrderId}..."`). If you're not familiar with regular expressions, using a complete/premade solution (such as the one David suggested) might be the better solution. If wanted, could try to write a short example later. – Mario May 10 '12 at 08:40