views:

469

answers:

8

Hi,

I'm trying to programmatically send an email of all of the dll files and their versions in a directory, recursively. I'd like to send the email as HTML output, using a table. Is there a good object-oriented way of doing this? I'd hate to write all of the tags by hand.

Something like:

private string getHTMLString()
{
    DirectoryInfo di = new DirectoryInfo("some directory");
    FileInfo[] files = di.GetFiles("*.dll", SearchOption.AllDirectories);
    foreach (FileInfo file in files)
    {
        Assembly assembly = Assembly.LoadFile(file.FullName);
        string version = assembly.GetName().Version.ToString();
    }
 }
+2  A: 

Tags are tags. You'll have to write them.

You can use an HtmlTextWriter to do the actual writing, but there's no magical way to avoid that.

John Saunders
brianary
I agree on the use of a DOM or HTML API where available.
John Saunders
+3  A: 

Something like this?

private string getHTMLString()
{
    DirectoryInfo di = new DirectoryInfo("some directory");
    FileInfo[] files = di.GetFiles("*.dll", SearchOption.AllDirectories);
    StringBuilder sb = new StringBuilder();
    sb.Append("<table>");
    foreach (FileInfo file in files)
    {
        Assembly assembly = Assembly.LoadFile(file.FullName);
        string version = assembly.GetName().Version.ToString();
        sb.AppendFormat("<tr><td>{0}</td><td>{1}</td></tr>", file.FullName, version);
    }
    sb.Append("</table>");
    return sb.ToString();

 }

Not really "object oriented" but I would argue the most logical.

DISCLAIMER: Compiled by hand

mletterle
+1, was writing the similar answer.
Li0liQ
+1, so was I. (Writing something similar, that is.)
Chris Miller
Seems like the most straightforward way. I thought of writing something like an HTMLTableClass, which uses a StringBuilder to do something like the above. I was really hoping for some magic built-in library, but I guess it's just not there. Thanks.
David Hodgson
I'd strongly recommend doing HtmlEncode() on the file name before embedding it in HTML.
Cylon Cat
-1: I'd make it -1/2 if I could. -1 for using AppendFormat without any HTML Encoding. This can produce incorrect results depending on the contents of file.FullName and version.
John Saunders
+1  A: 

What you are doing seems simple enough to simply do it by hand with a string buffer. If this were more complicated I'd suggest you use a template engine such as StringTemplate.

jcm
That won't handle special characters that need to be encoded.
John Saunders
What won't? If you need to encode something just use `HttpServerUtility.HtmlEncode`
jcm
+1  A: 

You can use the XmlWriter to do this in a more OO-fashion:

StringBuilder sb = new StringBuilder();
XmlWriter xmlWri = XmlWriter.Create(sb);
xmlWri.WriteStartElement("html");
{
    xmlWri.WriteStartElement("body");
    {
        xmlWri.WriteAttributeString("bgcolor", "black");

        // More html stuff
    }
    xmlWri.WriteEndElement(); // body
}
xmlWri.WriteEndElement(); // html
xmlWri.Flush();
xmlWri.Close();
string html = sb.ToString();

Edit: It does get rather cumbersome to do this, and in the future if you want to change the HTML content you'll have to do it by code. A better way is to read in a pre-formatted HTML document with place-holders that map to the columns in the table, so all you'd need is a way to loop through all the columns and do a find/replace against the place-holders in the HTML document.

arabian tiger
Yeah, I am personally of the opinion that the above is over-kill for the application. Using a template defined either in a file or the app.config is a good suggestion depending on the use case though.
mletterle
Templates can work, but you must be aware that you're not just working with strings. You must follow HTML encoding rules. Using a DOM or HTML API will do that part for you.
John Saunders
+4  A: 

You can actually use the HtmlTextWriter and use the HtmlTextWriterTag enum to create the tags, without actually having to hand write the "<" and ">".

Example:

StringBuilder sb = new StringBuilder();

using (HtmlTextWriter w = new HtmlTextWriter(new StringWriter(sb)))
{
      w.RenderBeginTag(HtmlTextWriterTag.P);

      w.AddStyleAttribute(HtmlTextWriterStyle.Color, "red");

      w.RenderBeginTag(HtmlTextWriterTag.Span);

      w.Write("This is some text");

      w.RenderEndTag();

      w.RenderEndTag();
 }

 string html = sb.ToString();

It doesn't automatically create the HTML for you, but it does help you write HTML in a "more OO" way and it will help you (not ensure) write valid HTML (all tags have closing tags, etc).

bdowden
A: 

I am not certain that you need an OO approach. Is your intent to create a table with a column that contains the directory of the dll, or will you add a column for each level of sub directory that you traverse?

bdowden has a nice solution that you could use with mletterle approach - you could always wrap this is a class, but I think that's overkill.

David Robbins
+7  A: 

It might be overkill, but you can use LINQ to XML to generate your HTML string... Here's your code, using LINQ and XElement to generate a simple table.

The only thing you need to be careful of is empty tags that are not valid as self-closing tags in HTML. For example, an empty TD: new XElement("td") will render as <td/> which is not valid HTML. You can fix this by inserting an empty string as content: new XElement("td", String.Empty) - this will output <td></td>.

private string GetHtmlString()
{
    DirectoryInfo di = new DirectoryInfo("some directory");
    FileInfo[] files = di.GetFiles("*.dll", SearchOption.AllDirectories);

    var container = new XElement("table",
        from file in files
        let assembly = Assembly.LoadFile(file.FullName)
        select new XElement("tr", 
            new XElement("td", file.FullName),
            new XElement("td", assembly.GetName().Version.ToString())
        )
    );

    return container.ToString();
 }
Joel Mueller
I like this, it's elegant :) No goofy for loops.
David Hodgson
+4  A: 

You can do it OO by instantiating a System.Web.UI.WebControls.Table, adding TableRows and TableCells and then calling Table.RenderControl into an HtmlTextWriter, but to be fair: That would suck :)

        var tbl = new System.Web.UI.WebControls.Table();

        DirectoryInfo di = new DirectoryInfo("some directory");
        FileInfo[] files = di.GetFiles("*.dll", SearchOption.AllDirectories);
        foreach (FileInfo file in files)
        {
            Assembly assembly = Assembly.LoadFile(file.FullName);
            string version = assembly.GetName().Version.ToString();

            var tr = new System.Web.UI.WebControls.TableRow();
            var tc = new System.Web.UI.WebControls.TableCell();
            tc.Text = HttpUtility.HtmlEncode(version);
            tr.Cells.Add(tc);
            tbl.Rows.Add(tr);
        }

        using (var ts = new StringWriter())
        using (var html = new System.Web.UI.HtmlTextWriter(ts))
        {
            // Not entirely sure about this part
            tbl.RenderControl(html);
            html.Flush();
            string htmlString = ts.ToString();
        }
Michael Stum
This is probably most "OO" way to do it. But yeah.. scary.
mletterle
It is rather verbose, but I'd say this is the best way to go about it if you don't want to use a template engine.
jcm