mike-obrien.net Resume Blog Labs
Tuesday, November 18, 2008

One thing I really love about Linq is how succinctly you can write import and export code. I've had to do this many times before Linq and it always felt kludgy no matter how clean you did it (Especially building xml documents). I think the decision to bake a set based syntax into C# and VB.NET was a great idea. So here is yet another Linq code sample, aren't there already a million of them out there??

Exporting data from a SQL Server table to xml using Linq2Sql and Link2Xml:

TestDataContext dataContext =
    new TestDataContext(@"Data Source=localhost;
        Initial Catalog=Test;Integrated Security=True");

XDocument export = new XDocument(
    new XElement(
        "clients",
            new XComment("This is a comment!"),
            from client in dataContext.Clients
            orderby client.State, client.City 
            select new XElement(
                "client",
                    new XAttribute("id", client.Id.ToString()),
                    new XElement("name", new XText(client.Name)),
                    new XElement("address", new XText(client.Address)),
                    new XElement("address2", new XText(client.Address2)),
                    new XElement("city", new XText(client.City)),
                    new XElement("state", new XText(client.State)),
                    new XElement("zip", new XText(client.Zip)),
                    new XElement("comments", new XCData(
                        string.Format("{0} is based out of {1}.", 
                        client.Name, client.State)))
                )
            )
        );
export.Save(@"D:\Temp\Clients.xml");

Importing data from an xml document to a SQL Server table using Linq2Xml and Linq2Sql (And as Anon pointed out we can do a direct cast since the XAttribute (And XElement) class define explicit conversion operators for primitive types. On that note we also dont need to check if the XElement or XAttribute is null. The Linq2Sql entity defines fields (Or properties) as nullable types and the XElement and XAttribute classes define conversion operators for nullable primitive types. So if an attribute or element does not exist the entity value is simply set to null.):

TestDataContext dataContext =
    new TestDataContext(@"Data Source=localhost;
        Initial Catalog=Test;Integrated Security=True");

XDocument document = XDocument.Load(@"D:\Temp\Clients.xml");

var clients = from client in document.Element("clients").Elements("client")
    select new Client()
    {
        Id = (int)client.Attribute("id"),
        Name = (string)client.Element("name"),
        Address = (string)client.Element("address"),
        Address2 = (string)client.Element("address2"),
        City = (string)client.Element("city"),
        State = (string)client.Element("state"),
        Zip = (string)client.Element("zip")
    };

foreach (var client in clients)
    dataContext.Clients.InsertOnSubmit(client);

dataContext.SubmitChanges();
Tuesday, November 18, 2008 2:10:56 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [2]  | 
Wednesday, October 22, 2008

I have been having XmlTraceListener woes... None of which are showstoppers just major time wasters! The following bug is fixed as of version 4.1! The first of which has to do the message. The message is not escaped and/or CDATA qualified. So if you have an ampersand or greater/less than sign in the message it will throw this cryptic error:

An error occurred while parsing EntityName. Line x, position y.

I ended up manually running the log entry object through the Format() method on the XmlLogFormatter class to get the raw xml and see what exactly it was complaining about. Sure enough there was an ampersand. Adding an extension method to the string class and manually escaping the LogEntry message alleviated this to some degree. Bad thing is though, the message is escaped for all listeners and you probably don't want to see HTML entities in your event log entry or email message. I think the only way around this would be to write your own xml trace listener.

namespace MyApp.Runtime.Extensions
{
    public static class String
    {
        public static string EscapeUnsafeXmlCharacters(this string value)
        {
            return value.Replace("<", "&lt;").Replace(">", "&gt;").Replace("&", "&amp;");
        }
    }
}

Issue #2 has to do with where the log file is saved. The FlatFileTraceListner actually saves the log file relative to the application root (Where the .config file is). Take a look at the RootFileNameAndEnsureTargetFolderExists() method in the FormattedTextWriterTraceListener class (The FlatFileTraceListener's base class). Before it passes the filename to it's base class constructor, it runs it through this method. The XmlTraceListener on the other hand does not follow this pattern which had me going nuts trying to figure out what I was doing wrong (Thinking that it just wasn't saving at all). I wasn't getting any errors from the XmlTraceListener (More on this below) so I started to get the feeling that perhaps it was writing to the log but just not where I expected. So I fired up Process Monitor and didn't see any log writes. Then I switched the app (It was a web application project BTW) to use the built in web server instead of IIS. Ran everything again and voilĂ :

image

So it was saving it somewhere else, but only when running under the built in web server. Looking at the code in reflector you can see that, unlike the FormattedTextWriterTraceListener, the XmlWriterTraceListener does not modify the path to be the application root before it passes it to it's base class constructor. So the directory ends up being that of the entry assembly. Bottom line is you have to supply an explicit path when you're using the XmlTraceListener in a web application.

This leads me to the third oddity; where it would save running under the builtin web server but not IIS. The base class, TextWriterTraceListener, calls the EnsureWriter() method (Shown below) before writing to the file. If it returns true it writes the entry, otherwise it doesn't. Notice that if there is a UnauthorizedAccessException it just returns false and then subsequently, in the calling Write method, silently skips writing. When I was using IIS as my web server (And the code was running as the Network Service account) I didn't see any exceptions and no log writes were showing up in Process Monitor. Which was very confusing! But when I switched over to using the built in web server (Which was obviously running as my interactive account) I saw the log writes. So when it was running under the Network Service account it obviously did not have permissions to save the log file to wherever it was trying to save it and no exception was raised... Very confusing behavior this. Reminds me of the advice in Jeffrey Richter's CLR Via C# book not to ever swallow exceptions! So keep that in mind, if you don't see any errors and log writes with the XmlTraceListner, it may be permissions and/or path related.

Well, hopefully this wasn't too long winded and hopefully it clears up some oddities with the XmlTraceListener.

public class TextWriterTraceListener : TraceListener
{
    public override void Write(string message)
    {
        if (this.EnsureWriter())
        {
            if (base.NeedIndent)
            {
                this.WriteIndent();
            }
            this.writer.Write(message);
        }
    }
    internal bool EnsureWriter()
    {
        bool flag = true;
        if (this.writer == null)
        {
            flag = false;
            if (this.fileName == null)
            {
                return flag;
            }
            Encoding encodingWithFallback = GetEncodingWithFallback(new UTF8Encoding(false));
            string fullPath = Path.GetFullPath(this.fileName);
            string directoryName = Path.GetDirectoryName(fullPath);
            string fileName = Path.GetFileName(fullPath);
            for (int i = 0; i < 2; i++)
            {
                try
                {
                    this.writer = new StreamWriter(fullPath, true, encodingWithFallback, 0x1000);
                    flag = true;
                    break;
                }
                catch (IOException)
                {
                    fileName = Guid.NewGuid().ToString() + fileName;
                    fullPath = Path.Combine(directoryName, fileName);
                }
                catch (UnauthorizedAccessException)
                {
                    break;
                }
                catch (Exception)
                {
                    break;
                }
            }
            if (!flag)
            {
                this.fileName = null;
            }
        }
        return flag;
    }

    // Other members removed for brevity...
}
Wednesday, October 22, 2008 3:54:38 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Wednesday, December 05, 2007

...and your 100% sure your xpath is correct. It's probably that there is a namespace defined. All you need to do is define the namespace under the xmlpoke element and prefix your element names with the namespace prefix. Check it:

web.config:

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
  <appSettings>
    <add key="SomeSetting" value="" />
  </appSettings>
</configuration>

NAnt:

<project name="YadaYadaYada" default="default">
    <target name="default">
        <xmlpoke
             file="D:\Temp\web.config"
             xpath="/ns:configuration/ns:appSettings/ns:add[@key='SomeSetting']/@value"
             value="SomeValue">
                  <namespaces>
                      <namespace prefix="ns" uri="
http://schemas.microsoft.com/.NetConfiguration/v2.0" />
                  </namespaces>

         </xmlpoke>
   </target>
</project>

Wednesday, December 05, 2007 8:00:25 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Tuesday, November 27, 2007

NAnt scripts can get cluttered with configuration values. One way to clean this up is to create properties that are used throughout the script. Although, you could take this a step further and store NAnt script configuration values in an Xml file. I like this approach because it creates a clear separation between the script and modifiable parameters. For example lets say we have an Xml configuration file called Build.config that contains paths:

<?xml version="1.0"?>
<buildConfiguration>
    <paths>
        <path name="SubversionPath" value="C:\Program Files\CollabNet Subversion Server\svn.exe" />
    </paths>
</buildConfiguration>

Our NAnt script could then load these paths into parameters in an "init" target. All other targets could then depend on this "init" target.

<?xml version="1.0"?>
<project name="Build" default="default">

    <property name="SubversionPath" value="" />
    <target name="init">
        <xmlpeek

              file="Build.config"
              xpath="/buildConfiguration/paths/path[@name = 'SubversionPath']/@value"
              property="SubversionPath"/>
     </target>

     <target name="default" depends="init">
         <echo message="${SubversionPath}" />

     </target>
</project>

image

Tuesday, November 27, 2007 5:09:38 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Tuesday, October 30, 2007

Below is a simple XML/XSL that displays the period table. The XML contains a number of properties not displayed by the XSL so it can be expanded.

Elements.xml (119.87 KB)
PeriodicTable.xsl (3.73 KB)

Tuesday, October 30, 2007 4:32:36 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Tuesday, October 09, 2007

I love WiX, but I hate that you have to specify the id's and short filenames! This really slows you down when you have a lot of files to add. I have read that these may be optional in v3 and auto generated by the compiler. Until then you could use this Visual Studio addin that that allows you to randomly generate and insert id's and short filenames as well as insert some common tags with the aformentioned attributes randomly filled in. n-joy!

Visual Studio 2005

Installer: WiXHelperAddin.EXE (217.21 KB)
Source: WiXHelperSource.zip (258.91 KB)

Visual Studio 2008

Installer: WiXHelperAddin2008.EXE (217 KB)
Source: WiXHelperSource2008.zip (63.89 KB)

PS: Also, check out the GUID inserter (Which also appears in the image below...); this is very helpful for WiX development!

image

Tuesday, October 09, 2007 1:41:54 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Thursday, April 12, 2007

So I am trying to write log information to an XML document using the XmlTextWriter.  When it reaches a certian size I want it closed out and archived. What I mean by "closed out" is that while the log file is "open" the root element is not closed so that I can keep adding entries over a period of time. In other words:

<?xml version="1.0"?>
<log>
    <entry date="1/1/2007">Yada yada yada</entry>
    <entry date="1/1/2007">Yada yada yada</entry>
    ...


Then once its full I will write the closing root element and rename it:

<?xml version="1.0"?>
<log>
    <entry date="1/1/2007">Yada yada yada</entry>
    <entry date="1/1/2007">Yada yada yada</entry>
    ...
    <entry date="1/1/2007">Yada yada yada</entry>
</log>

Well the XmlTextWriter automatically closes out all open elements when you close it. Looking in Reflector reveals what is happening behind the scenes:

public override void Close()
{
   try
   {
      this.AutoCompleteAll();
   }
   catch {}
   finally
   {
      this.currentState = State.Closed;
      this.textWriter.Close();
   }
}

First the AutoCompleteAll() method is called, then the underlying stream is closed. The AutoCompleteAll() method is as follows:

private void AutoCompleteAll()
{
   if (this.flush)
   {
      this.FlushEncoders();
   }
   while (this.top > 0)
   {
      this.WriteEndElement();
   }
}

Doesent look like there is any official way to explicitly close only the elements you want to close, its just automatically done for you when you close the writer. The only work around I could figure out is to explicitly close the elements I want closed, then close the base stream (Not the XmlTextReader). This works but IMO its a little hackey since the code in the Close() method could potentially change in the future and you could end up circumventing some code that you might want executed when your done with the reader.

.NET | C# | VB.NET | XML/XSL
Thursday, April 12, 2007 7:01:42 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Thursday, December 21, 2006

The XSL below illustrates how to alternate color using the mod operator and setting the style class. This allows to to create a general ledger look.

    <xsl:template match="log/entry">
        <div>
            <xsl:if test="position() mod 2 = 1">
                <xsl:attribute name="class">LogEntryHighlight</xsl:attribute>
            </xsl:if>
            <xsl:if test="position() mod 2 = 0">
                <xsl:attribute name="class">LogEntry</xsl:attribute>
            </xsl:if>
            <xsl:value-of select="."/>
        </div>    
    </xsl:template> 

Thursday, December 21, 2006 9:11:59 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |