We're starting to focus on the startup time of our current application. We're sitting at about 5-8 seconds, and that's pretty good for what we're doing; however, every time we throw an exception (obviously, it gets caught), we go on a massive bug hunt.
Some of most egregious examples are System.FileNotFoundExceptions. These were cropping up in the strangest places. I started to track them down to all of our XmlSerialization.
I'll say this about Visual Studio and .NET 3.5. I've really enjoyed coding in it during the last year. It's better than Java, hands down (that's a topic for another post). However, there are some corners where Microsoft has just left stuff to whither and die, and how Xml is deserialized is one such place.
So, here's a brief overview.
- Run into a piece of code like
XmlSerializer ser = new XmlSerializer(typeof(ThingToDeserialize)); this.Thing = (ThingToDeserialize)ser.Deserialize(fs); - Look for a companion assembly to the one containing
ThingToDeserializecalled {AssemblyName}.XmlSerializers.dll that contains the code to do the deserialization. - When you can't find it, dynamically generate the deserialization code
- Compile it on the fly to a temporary assembly.
- When you finish running the program, dispose of the above.
As you can guess, the last two steps might be a bit time consuming and wasteful. So, what to do?
Well, my natural response was to look for a way to generate those {AssemblyName}.XmlSerializers.dll files at build time, like everything else in the project. You'd think that Visual Studio would provide this functionality, and you might go looking for it in the build tab of the project you are working on. You might even think that "Generate Serialization Assembly: On" might be the thing you're looking for:

You'd think wrong. This does nothing. If we build the application, we see the following in the build output.
------ Build started: Project: MyProject, Configuration: Debug Any CPU ------ C:\WINDOWS\Microsoft.NET\Framework\v3.5\Csc.exe .... Compile complete -- 0 errors, 0 warnings C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\sgen.exe /assembly:C:\Scimatic\Project\svn\trunk\Source\MyProject\obj\Debug\MyProject.dll /proxytypes /reference:"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll"
Looking at the build output, we see that running this option runs the sgen.exe tool against our assembly. However, it runs it with /proxytypes commandline switch, which seems only useful to folks who do web development, as it is used to generate types for "proxy classes and web method parameters".
So where does that leave us? The next step is to look at the command line options for sgen.exe
Usage: sgen.exe [[/assembly:] | [ ]] [/type:] [/reference:] [/compiler:] [/debug] [/keep] [/nologo] [/silent] [/verbose] Developer options: /assembly: Assembly location or display name. Short form is '/a:'. /type: Generate code for serialization/deserialization of a single type from the input assembly. Short form is '/t:'. /reference: Reference metadata from the specified assembly files. Short form is '/r:'. /compiler: Visual C# compiler options to use while compiling generated code. Short form is '/c'. For complete list of available options see c# compiler help. /proxytypes Generate serialization code only for proxy classes and web method parameters. Short form is '/p'. /debug Generate image which can be used under a debugger. Short form is '/d'. /keep Keep source code and compiler temp files. Short form is '/k'. /force Forces overwrite of a previously generated assembly. Short form is '/f'. /out: Output directory name (default: target assembly location). Short form is '/o:'. /parsableerrors Print errors in a format similar to those reported by compilers. Miscellaneous options: /? or /help Show this message /nologo Prevents displaying of logo. Short form is '/n'. /silent Prevents displaying of success messages. Short form is '/s'. /verbose Displays verbose output for debugging. Short form is '/v'. List types from the target assembly that cannot be serialized with XmlSerializer.
Okay, not so bad. I tried the next logical thing. Generate XmlSerializers for the types in my assembly.
Oops. Can't do it. sgen barfs on the types that aren't marked as serializable. Like INTERFACES.
Okay, now back to the command line; I have multiple types per assembly to serialize, let's do the straight-forward thing and add multiple /t: parameters to the command line.
Oops. It only takes a single /t argument.
Here's where I repeat that Microsoft has left some corners of Visual Studio to whither and die.
Fortunately, this is a case where having multiple implementations is really a win. (Okay, it's a win in almost all cases). The folks over at Mono have created their own implementation of sgen. Mono's implementation allows for multiple /t options on the command line. And the source is available so you can tweak it however you want. That's what I did to get it to ignore interface types in my assemblies.
So now I add the "sgenplus" as a post-build event in all the projects that have Xml serialization. Works like a charm.

