Search code examples
c#vb.netstringbuilder

How can I combine multi line appends in VB.Net 12 into a single AppendFormat


I am using VB.Net 12, so multi-line strings are not allowed as they are in C#.

I have following code in VB.Net, which I need to convert into a single AppendFormat. This is very easy in C# since the long string can be spread over multiple lines for better readability/maintenance, but it doesn't seem possible in VB.Net without concatenating string using the & operator; but then the purpose of using StringBuilder gets defeated.

Question

Can I convert following VB.Net to use a single AppendFormat without using string concatenation operator &?

'use a stringbuilder for better string performance
Dim sb As New StringBuilder()
sb.Append("<html>")
sb.Append("<head><style>body {font-size:10pt; font-family:Arial, Sans Serif;} table{margin:0; padding:0; border-collapse:collapse;} td{border:solid 1px #eee; padding:2px;}")
sb.Append(".m{color:#f00; font-weight:bold; font-size:14pt; margin-bottom:9px;} .t{width:120px; font-weight:bold; font-size:8pt;} .d{width:500px;}")
sb.Append("</style></head>")
sb.Append("<body>")
sb.AppendFormat("<div class='m'> {0} </div><table>", ex.Message.ToString())
sb.AppendFormat("<tr><td class='t'>REQUEST URL</td><td class='d'> {0} </td></tr>", currentContext.Request.Url.OriginalString)
sb.AppendFormat("<tr><td class='t'>REQUEST PATH</td><td class='d'> {0} </td></tr>", currentContext.Request.Path)
sb.AppendFormat("<tr><td class='t'>QUERY STRING</td><td class='d'> {0} </td></tr>", currentContext.Request.QueryString.ToString())
sb.AppendFormat("<tr><td class='t'>TARGET SITE</td><td class='d'> {0} </td></tr>", ex.TargetSite.Name)
sb.AppendFormat("<tr><td class='t'>STACK TRACE</td><td class='d'> {0} </td></tr>", ex.StackTrace)

sb.AppendFormat("<tr><td class='t'>USERID</td><td class='d'> {0} </td></tr>", userId)
sb.AppendFormat("<tr><td class='t'>USER</td><td class='d'> {0} </td></tr>", userName)
sb.Append("</table>")
sb.Append("</body>")
sb.Append("</html>")

UPDATE

I got it to work using Steve's answer in which he suggested to use XML element. The only thing I needed to do was to escape curly braces by preceding each curly brace in expression with the same curly brace. The working code is as below.

   Dim s As XElement = <html>
                            <head>
                                <style>
          body{{font-size:10pt; font-family:Arial, Sans Serif;}} 
           table{{margin:0; padding:0; border-collapse:collapse;}}
            td{{border:solid 1px #eee; padding:2px;}}
            .m{{color:#f00; font-weight:bold; font-size:14pt; margin-bottom:9px;}}
           .t{{width:120px; font-weight:bold; font-size:8pt;}}
           .d{{width:500px;}}
         </style>
                            </head>
                            <body>
                                <div class='m'>{0}</div>
                                <table>
                                    <tr><td class='t'>REQUEST URL</td><td class='d'>{1}</td></tr>
                                    <tr><td class='t'>REQUEST PATH</td><td class='d'>{2}</td></tr>"

<tr><td class='t'>QUERY STRING</td><td class='d'>{3}</td></tr>"
<tr><td class='t'>TARGET SITE</td><td class='d'>{4}</td></tr>
                                        <tr><td class='t'>STACK TRACE</td><td class='d'>{5}</td></tr>
                                        <tr><td class='t'>USERID</td><td class='d'>{6}</td></tr>
                                        <tr><td class='t'>USER</td><td class='d'>{7}</td></tr>
                                    </table>
                                </body>
                            </html>
        email.Body = sb.AppendFormat(s.ToString(), ex.Message.ToString(), _
                                 currentContext.Request.Url.OriginalString, _
                                 currentContext.Request.Path, _
                                 currentContext.Request.QueryString.ToString(), _
                                 ex.TargetSite.Name, ex.StackTrace, userId, userName).ToString()

Performance Update

I tried all 3 approaches discussed here to find which one is best performing in my scenario. So, I ran my ASP.Net scenario with each of these approaches (only one was implemented for each test)

  • TEST 1: XElement with StringBuiler.Format
  • TEST 2: XElement with inline expressions
  • TEST 3: StringBuilder with multiple Appends and AppendFormat as in code given in my question

What I found using ticks elapsed of Stopwatch object for each run are as below. I ran my ASP.Net scenario 4 times for each approach. And it seems that the third approach of StringBuilder is the fastest.

  • TEST 1: XElement with StringBuilder format : 6505, 657, 426, 446
  • TEST 2: XElement with inline expressions : 6326, 414, 422, 635
  • TEST 3: StringBuilder appends/appendformats : 6588, 351,345, 280

I would think XElement provides better readability when someone is reading/maintaining your code, but the XElement approaches fail to beat StringBuilder in performance.


Solution

  • Here your code with XML Literals. I have tried to use directly a string but for some reason the result gets truncated and some part of the original text are missing. However using an XElement it works correctly

    Dim s As XElement = 
    <html>  
        <head>  
            <style>  
                body {font-size:10pt; font-family:Arial, Sans Serif;} 
                table {margin:0; padding:0; border-collapse:collapse;} 
                td{border:solid 1px #eee; padding:2px;} 
                .m{color:#f00; font-weight:bold; font-size:14pt; margin-bottom:9px;}  
                .t{width:120px; font-weight:bold; font-size:8pt;} 
                .d{width:500px;} 
            </style> 
        </head> 
        <body> 
            <div class='m'><%= ex.Message %></div> 
            <table> 
                <tr> 
                    <td class='t'>REQUEST URL</td> 
                    <td class='d'><%= currentContext.Request.Url.OriginalString %></td> 
                </tr> 
                <tr> 
                    <td class='t'>REQUEST PATH</td> 
                    <td class='d'><%= currentContext.Request.Path %></td> 
                </tr> 
                <tr> 
                    <td class='t'>QUERY STRING</td> 
                    <td class='d'><%= currentContext.Request.QueryString.ToString() %></td> 
                </tr> 
                <tr> 
                    <td class='t'>TARGET SITE</td> 
                    <td class='d'><%= ex.TargetSite.Name %></td> 
                </tr> 
                <tr> 
                    <td class='t'>STACK TRACE</td> 
                    <td class='d'><%= ex.StackTrace %></td> 
                </tr> 
                <tr> 
                    <td class='t'>USERID</td> 
                    <td class='d'><%= userId %></td> 
                </tr> 
                <tr> 
                    <td class='t'>USER</td> 
                    <td class='d'><%= userName %></td> 
                </tr> 
            </table> 
        </body> 
    </html> 
    

    We can use a double curly braces around the braces in the style block and then use string.Format to replace the parameters. (Partial block taken from the code above)

    ....
    body {{font-size:10pt; font-family:Arial, Sans Serif;}} 
    ....
    

    At this point we could use the String.Format to resolve in the code behind the formatting for the whole text

    Dim text = String.Format(s.ToString(), ex.Message, .........)