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.
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);
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).
Related
It is possible to generate permanent pages with asp.net with stuff stored on database?
For example a few locations/types and i want to do something like the single example:
Location: New York
Type: Car
Generated Permanent Page With information:
Cars in New York
Generated link:
mywebsite.com/cars/newyork
At the moment i've done a search filter displays the results based on Selected Location and Selected type.
Hope you can give me some tips about this.
One approach is to save HTML template in db and then use string.Format to insert values
e.g like that
string name = "test";
int age = 167;
string test = "{0} format {1}";
string[] args = new string[] { name, age.ToString() };
Console.WriteLine(string.Format(test, args));
prints: test format 167
Then you return View with string that is your html with inserted values and then you "display" (allow browser to render) it using
#model string
#Html.Raw(#Model)
https://learn.microsoft.com/en-us/dotnet/api/system.web.mvc.htmlhelper.raw?view=aspnet-mvc-5.2
But be careful, because it may be very dangerous if user is able to provide/manipulate those values. Read about XSS attack.
Let's say I have a table called Couriers in my database and an Entity Framework class also called Couriers (DbContext.Couriers), with this example data:
Id: 1. Name: UPS
Id: 2. Name: DHL
I have extended this class with two subclasess CourierUPS and CourierDHL, and both two have a new method called PrintSticker(), which is different in UPS and DHL (each company needs a different sticker format).
I also have a view with a combo to select between UPS or DHL and a button that calls the PrintSticker method of the selected Courier.
Well, the problem is that I can create the object with an IF statement (if selectedCourier = "UPS" create new CourierUPS elseif selectedCourier = "DLH" create new CourierDHL... it's just pseudo code), but it is not a good way, because if I want to create FedEx courier in the future, I will need to change that IF statement, so I think I will become a mess.
What I want to achieve is a way to dinamycally obtain this:
C#
CourierUPS courier = new CourierUPS;
VB.NET
Dim courier as CourierUPS = new CourierUPS
The questions are:
1) Is there a simple way to save the name of the class in the table (CourierUPS, CourierDHL, etc.), and use it the code to create the new objects?
2) Is there a simple way to dynamically construct the name of the class, attaching the name of the courier (DHL or UPS) to the "Courier" word? Courier + UPS = CourierUPS, so our code will use that class.
Both C# or VB.NET solutions would be appreciatted.
Thank you
And option that you can try is to create a factory dictionary for creating each type.
Start out with this:
var factory = new Dictionary<string, Func<Courier>>()
{
{ "UPS", () => new CourierUPS() },
{ "DHL", () => new CourierDHL() },
};
Now you can create either the "UPS" or "DHL" courier like this:
var courier = factory["UPS"].Invoke();
But, if you now want to configure a new courier you can pass your dictionary into a method that can add in "FedEx", and you can add it like so:
factory["FedEx"] = () => new CourierFedEx();
Now you can do this back in your main code:
var courier = factory["FedEx"].Invoke();
It's basic, but it works.
I am trying to loop through a Dataset, creating a page per item using Aspose.Words Mail-Merge functionality. The below code is looping through a Dataset - and passing some values to the Mail-Merge Execute function.
var blankDocument = new Document();
var pageDocument = new Document(sFilename);
...
foreach (DataRow row in ds.Tables[0].Rows){
var sBarCode = row["BarCode"].ToString();
var imageFilePath = HttpContext.Current.Server.MapPath("\\_temp\\") + sBarCode + ".png";
var tempDoc = (Document)pageDocument.Clone(true);
var fieldNames = new string[] { "Test", "Barcode" };
var fieldData = new object[] { imageFilePath, imageFilePath };
tempDoc.MailMerge.Execute(fieldNames, fieldData);
blankDocument.AppendDocument(tempDoc, ImportFormatMode.KeepSourceFormatting);
}
var stream = new MemoryStream();
blankDocument.Save(stream, SaveFormat.Docx);
// I then output this stream using headers,
// to cause the browser to download the document.
The mail merge item { MERGEFIELD Test } gets the correct data from the Dataset. However the actual image displays page 1's image on all pages using:
{ INCLUDEPICTURE "{MERGEFIELD Barcode }" \* MERGEFORMAT \d }
Say this is my data for the "Barcode" field:
c:\img1.png
c:\img2.png
c:\img3.png
Page one of this document, displays c:\img1.png in text for the "Test" field. And the image that is show, is img1.png.
However Page 2 shows c:\img2.png as the text, but displays img1.png as the actual image.
Does anyone have any insight on this?
Edit: It seems as this is more of a Word issue. When I toggle between Alt+F9 modes inside Word, the image actually displays c:\img1.png as the source. So that would be why it is being displayed on every page.
I've simplified it to:
{ INCLUDEPICTURE "{MERGEFIELD Barcode }" \d }
Also, added test data for this field inside Word's Mailings Recipient List. When I preview, it doesn't pull in the data, changing the image. So, this is the root problem.
I know this is old question. But still I would like to answer it.
Using Aspose.Words it is very easy to insert images upon executing mail merge. To achieve this you should simply use mergefield with a special name, like Image:MyImageFieldName.
https://docs.aspose.com/words/net/insert-checkboxes-html-or-images-during-mail-merge/#how-to-insert-images-from-a-database
Also, it is not required to loop through rows in your dataset and execute mail merge for each row. Simply pass whole data into MailMerge.Execute method and Aspose.Words will duplicate template for each record in the data.
Here is a simple example of such template
After executing mail merge using the following code:
// Create dummy data.
DataTable dt = new DataTable();
dt.Columns.Add("FirstName");
dt.Columns.Add("LastName");
dt.Columns.Add("MyImage");
dt.Rows.Add("John", "Smith", #"C:\Temp\1.png");
dt.Rows.Add("Jane", "Smith", #"C:\Temp\2.png");
// Open template, execute mail merge and save the result.
Document doc = new Document(#"C:\Temp\in.docx");
doc.MailMerge.Execute(dt);
doc.Save(#"C:\Temp\out.docx");
The result will look like the following:
Disclosure: I work at Aspose.Words team.
If this was Word doing the output, (not sure about Aspose), there would be two possible problems here.
INCLUDEPICTURE expects backslashes to be doubled up, e.g. "c\\img2.png", or (somewhat less reliable) to use forward slashes, or Mac ":" separators on that platform. It may be OK if the data comes in via a field result as you are doing here, though.
INCLUDEPICTURE results have not updated automatically "by design" since Microsoft modified a bunch of field behaviors for security reasons about 10 years ago. If you are merging to an output document, you can probably work around that by using the following nested fields:
{ INCLUDEPICTURE { IF TRUE "{ MERGEFIELD Barcode }" } }
or to remove the fields in the result document,
{ IF { INCLUDEPICTURE { IF TRUE "{ MERGEFIELD Barcode }" } } {
INCLUDEPICTURE { IF TRUE "{ MERGEFIELD Barcode }" } } }
All the { } need to be inserted with Ctrl+F9 in the usual way.
(Don't ask me where this use of "TRUE" is documented - as far as I know, it is not.)
I am using this code to retrieve the values I want from XML:
IEnumerable<ForewordReview> reviews = null;
try
{
reviews = from item in xmlDoc.Descendants("node")
select new ForewordReview()
{
PubDate = item.Element("created").ToString(),
Isbn = item.Element("isbn").ToString(),
Summary = item.Element("review").ToString()
};
} // ...
Incidentally, a client is now passing us almost every tag with CDATA which I need to extract:
<review>
<node>
<created>
<![CDATA[2012-01-23 12:40:57]]>
</created>
<isbn>
<![CDATA[123456789]]>
</isbn>
<summary>
<![CDATA[Teh Kittehs like to play in teh mud]]>
</summary>
</node>
</review>
I have seen a couple of solutions for extracting these values from within the CDATA tag, one of which is to use a where clause on the LINQ statement:
where element.NodeType == System.Xml.XmlNodeType.CDATA
I sort of see whats going on here, but I am not sure this works with how I am using Linq (specifically, building an object from selected items.
Do I need to apply this filter on the items in the select statement individually? Otherwise, I dont really understand how this will work with the code I am using.
As always, I appreciate the help.
Cast each XElement to a string instead:
reviews = from item in xmlDoc.Descendants("node")
select new
{
PubDate = (string)item.Element("created"),
Isbn = (string)item.Element("isbn"),
Summary = (string)item.Element("summary")
};
// Output:
// {
// PubDate = 2012-01-23 12:40:57,
// Isbn = 123456789,
// Summary = Teh Kittehs like to play in teh mud
// }
This also works with other data types, such as int, float, DateTime, etc:
reviews = from item in xmlDoc.Descendants("node")
select new
{
PubDate = (DateTime)item.Element("created")
};
// Output:
// {
// PubDate = 1/23/2012 12:40:57
// }
It also works with XAttributes as well.
Remember that there is no difference between the meaning of:
<a>
<b>Hello</b>
<c>& hello again</c>
</a>
and of
<a>
<b><![CDATA[Hello]]></b>
<c><![CDATA[& hello again]]></c>
</a>
Since you're calling ToString() and getting the entire content back - opening and closing tags, entity references, etc. still in XML form, then you must be prepared to deal with it in XML form. If not, the problem isn't with the code you show here, it's with the code that was okay with PubDate being "<created>2012-01-23 12:40:57</created>" and now
isn't okay with it being the exactly equivalent "";
Either change that code to really parse the XML (for which the framework offers lots of things to help), or change it to take the date on its own and use Element("created").Value to retrieve it.
We have bunch of Domain Entities which should be rendered to an html format, which shows their detail in a pop up window.
I would be glad to do something like this:
Product product = new Product(...);
product.ToHtml(); // or: HtmlRenderer.Render(Product);
but my main problem is how to do this stuff from behind.
I have 3 different answers:
1. Render By Code:
I can simply write my code for rendering the Html inside the ToHtml Method (C#) - the problem it is that it is too static. if you would like to move a little bit the header to the middle you should change code.
moreover, it is very hard to read Html indentation in C#.
2. Using XSL:
XSL Files can easily manage the Html template and using XSLT I can transform XML file to the right place of the documents.
the parser already written by someone else (just need to learn the syntax)
** for this we will need that each object could serialize to Xml. and if the object changed -> the Xml will be changed --> the xslt need to be changed too
** this will also give me the option to indent the html easily for example: adding css capabilities and\or change the html design
3. using other template engine:
Write my own C# -> Html template Engine so it will read the template from file (*.template) and will insert the right property in the right place of the template using reflection.
** in this solution we have many issues that we can think of, for example: how the syntax should be like?
is this thing ok?
%Name% %Description%
and how we can handle arrays?
** maybe we can use an existing engine (Brail or T4-Templating)?
What do you prefer?
do you know a good engine?
for now I prefer the second solution, but it gonna be very slow.
thanks
I once had a need to render a collection of any type to an Html Table. I created an extension method on IEnumerable<T> that had overloads for css and the like. You can see my blog post about it here:
http://crazorsharp.blogspot.com/2009/03/cool-ienumberable-extension-method_25.html
It uses reflection to get all the properties of the object, and renders a nice little table. See if that would work for you.
[System.Runtime.CompilerServices.Extension()]
public string ToHtmlTable<T>(IEnumerable<T> list, string propertiesToIncludeAsColumns = "")
{
return ToHtmlTable(list, string.Empty, string.Empty, string.Empty, string.Empty, propertiesToIncludeAsColumns);
}
[System.Runtime.CompilerServices.Extension()]
public string ToHtmlTable<T>(IEnumerable<T> list, string tableSyle, string headerStyle, string rowStyle, string alternateRowStyle, string propertiesToIncludeAsColumns = "")
{
dynamic result = new StringBuilder();
if (String.IsNullOrEmpty(tableSyle)) {
result.Append("<table id=\"" + typeof(T).Name + "Table\">");
} else {
result.Append((Convert.ToString("<table id=\"" + typeof(T).Name + "Table\" class=\"") + tableSyle) + "\">");
}
dynamic propertyArray = typeof(T).GetProperties();
foreach (object prop in propertyArray) {
if (string.IsNullOrEmpty(propertiesToIncludeAsColumns) || propertiesToIncludeAsColumns.Contains(prop.Name + ",")) {
if (String.IsNullOrEmpty(headerStyle)) {
result.AppendFormat("<th>{0}</th>", prop.Name);
} else {
result.AppendFormat("<th class=\"{0}\">{1}</th>", headerStyle, prop.Name);
}
}
}
for (int i = 0; i <= list.Count() - 1; i++) {
if (!String.IsNullOrEmpty(rowStyle) && !String.IsNullOrEmpty(alternateRowStyle)) {
result.AppendFormat("<tr class=\"{0}\">", i % 2 == 0 ? rowStyle : alternateRowStyle);
} else {
result.AppendFormat("<tr>");
}
foreach (object prop in propertyArray) {
if (string.IsNullOrEmpty(propertiesToIncludeAsColumns) || propertiesToIncludeAsColumns.Contains(prop.Name + ",")) {
object value = prop.GetValue(list.ElementAt(i), null);
result.AppendFormat("<td>{0}</td>", value ?? String.Empty);
}
}
result.AppendLine("</tr>");
}
result.Append("</table>");
return result.ToString();
}
I can't agree more with John Feminella. Integrating Html rendering directly into your domain entities is a horrid pollution of your domain with a completely arbitrary and external concern. Like John said, you will make your entities very brittle by doing that, and break both of those critical rules: Separation of Concerns and Single Responsibility.
From the choices you gave, #3 is the closest to an appropriate way to do it. You need not write your own template engine. There are plenty free and ready-made template engines on the net that will do the job more than adequately (NVelocity, StringTemplate, Brail, etc. etc.)
Keep rendering where it belongs...in your presentation/view, and don't pollute your domain with higher level concerns.
This is the job of your view, not your controllers or models. As you suspect, using method (1) will make your changes very brittle. Instead, write a view fragment for each way in which you'd like to render a particular domain entity. Then simply pull in the appropriate views to build your final page.
I sincerely prefer method A. The HTML is already a standard so you shouldn't beat yourself up too much with intermediary formats such as XML (via XSL - method 2) or a custom format (method 3).
If writing in/for ASP.NET MVC you could however build an .ASCX (user control) that would accept an object of your type and render the appropiate HTML. And you don't get the nasty C# indentation and no highlighting nor autocomplete.
You could also write multiple User views to accomodate different scenarios for one object type.
Is there a reason you do not want to create a custom control .ascx file that takes domain entities object as an argument and then you can make the gui any way you want. This way you could dynamically add the controls to the page and just set a property to populate them.
Edit 1:
Just render the control to an HTMLTextWriter instead of placing it on the page.
StringBuilder outString = new StringBuilder();
StringWriter outWriter = new StringWriter(outString);
HtmlTextWriter htmlText = new HtmlTextWriter(outWriter);
System.Web.UI.WebControls.Label lbl1 = new System.Web.UI.WebControls.Label();
lbl1.Text = "This is just a test!";
lbl1.ToolTip = "Really";
lbl1.RenderControl(htmlText);
ViewData["Output"] = outString.ToString();
Output
<span title="Really">This is just a test!</span>
Of course use your custom control instead of a built in control but this was a quick and easy demo.
After bit of research i made my own function to convert Any Object toHtmlString.
VB .Net Code:
Public Function ToHtmlString(ByVal fObj As Object) As String
Dim pType = fObj.GetType()
Dim props As IList(Of PropertyInfo) = pType.GetProperties()
Dim value As Object = Nothing
Dim htmlString As String = "<html><body><form>"
For Each p In props
value = p.GetValue(fObj, Nothing)
If value <> Nothing Then
htmlString += String.Format("<input type=""hidden"" name=""{0}"" id=""{0}"" value=""{1}""/>", p.Name, value)
End If
Next
htmlString += "</form></body></html>"
Return htmlString.ToString()
it will get the property name and value from a particular object. you can manipulate html based on your
Personally, I'd combine method two and three: Just create a generic XML document using reflection, say:
<object type="xxxx">
<property name="ttt" value="vvv"/>
...
</object>
and use an XSTL stylesheet to create the actual HTML from this.