7

I'm currently refactoring a console application whose main responsibility is to generate a report based on values stored in the database.

The way I've been creating the report up til now is as follows:

const string format = "<tr><td>{0, 10}</td><td>
                       {1}</td><td>{2, 8}</td><td>{3}</td><td>{4, -30}</td>
                       <td>{5}</td><td>{6}</td></tr>";

if(items.Count > 0)
{
    builder.AppendLine(
        String.Format(format, "Date", "Id", "WorkItemId", 
                      "Account Number", "Name", "Address", "Description"));
}

foreach(Item item in items)
{
    builder.AppendLine(String.Format(format, item.StartDate, item.Id,
                       item.WorkItemId, item.AccountNumber, 
                       String.Format("{0} {1}", 
                                     item.FirstName, item.LastName), 
                       item.Address, item.Description));
}

string report = String.Format("<html><table border=\"1\">{0}
                                     </table></html>",
                              builder.ToString());

(The above is just a sample...and sorry about the formatting...I tried to format it so it wouldn't require horizontal scrolling....)

I really don't like that way I've done this. It works and does the job for now...but I just don't think it is maintainable...particularly if the report becomes any more complex in terms of the html that needs to be created. Worse still, other developers on my team are sure to copy and paste my code for their applications that generate an html report and are likely to create a horrible mess. (I've already seen such horrors produced! Imagine a report function that has hundreds of lines of hard coded sql to retrieve the details of the report...its enough to make a grown man cry!)

However, while I don't like this at all...I just can't think of a different way to do it.

Surely there must be a way to do this...I'm certain of it. Not too long ago I was doing the same thing when generating tables in aspx pages until someone kindly showed me that I can just bind the objects to a control and let .NET take care of the rendering. It turned horrible code, similar to the code above, into two or three elegant lines of goodness.

Does anyone know of a similar way of creating the html for this report without hard-coding the html?

mezoid
  • 28,090
  • 37
  • 107
  • 148

7 Answers7

10

Make your app to produce XML file with raw data. Then apply an external XSLT to it which would contain HTML.

More info: http://msdn.microsoft.com/en-us/library/14689742.aspx

GSerg
  • 76,472
  • 17
  • 159
  • 346
  • I'll give this a try and see how I go. Never worked with XSLT before so it'll take me a bit of time to come up to speed with it... Also, I'll hold off selecting an answer until I've seen it in action... – mezoid Mar 30 '09 at 04:51
4

You could use a template engine like NVelocity to separate your report view and your code. There are probably other decent template engines out there...

markt
  • 5,126
  • 31
  • 25
  • NVelocity is a good one... consistent with other template engines too – jle Mar 30 '09 at 00:18
  • Also, consider StringTemplate, which is what the ANTLR parser/compiler generator uses. I've used it for similar problems and it was a great fit. http://stringtemplate.org/ – Charlie Flowers Mar 30 '09 at 02:16
3

meziod - Another avenue to peruse is extension methods to the HtmlTextWriter object. I found a brilliant stab at just this on this very site.

HtmlTextWriter extension

I'm certain that you could leverage great potential from that...

regards - coola

Community
  • 1
  • 1
  • +1 for mentioning HtmlTextWriter and adding the link to the extension method-post. I know this is an old question, but I'd like to add that extension-methods are extremely useful! – NotImplementedException Apr 28 '17 at 09:52
2

Well, you could use one of the report frameworks (Crystal, MS RDL, etc) and export as html - however, I suspect that for simple data your current approach is less overhead. I might use an XmlWriter or LINQ-to-XML (rather than string.Format, which won't handle escaping)...

        new XElement("tr",
            new XElement("td", item.StartDate),
            new XElement("td", item.Id),
            new XElement("td", item.WorkItemId),

etc. Escaping is especially important for text values (name, description, etc).

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
1

You might consider using a simple template engine such as http://www.stefansarstedt.com/templatemaschine.html and separate your template from the content.

This is quite practical, allows template modification without recompiling and you still got C# power in your templates.

dr. evil
  • 26,944
  • 33
  • 131
  • 201
0

As coolashaka already mentioned, using the HtmlTextWriter is a good option, certainly if you add some useful extension methods, for example:

Simple example:

public static void WriteNav(this
     HtmlTextWriter writer, List<String> navItems)
    {
        writer.RenderBeginTag("nav");
        writer.RenderBeginTag(HtmlTextWriterTag.Ul);
        foreach (var item in navItems)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.Ul);
            writer.AddAttribute(HtmlTextWriterAttribute.Href, "~/" + item + ".html");
            writer.RenderBeginTag(HtmlTextWriterTag.A);
            writer.Write(item);
            writer.RenderEndTag();
            writer.RenderEndTag();
        }
        writer.RenderEndTag();
        writer.RenderEndTag();

    }

I know this is an old question, but Google will keep directing people here for years to come.

0

Microsoft SQL Reporting Services does this quite well, and can do multiple formats.

My company uses it to create PDF reports and since we have HIPAA requirements, we automatically put a password to it via a third party PDF control...

irperez
  • 1,771
  • 2
  • 22
  • 26