Monday, February 05, 2007

VS2005 - When SGEN Doesn't Work...

One of the features I was looking forward to making use of in .NET 2.0 was that of pre-generating the xml serialization assemblies, such as described in this post.

Using the sgen.exe tool is fairly straightforward, and there is an <SGen> MSBuild task for this purpose as well. However there is also one other option which not many people seem to have discussed the problem with that I could find.

If you look on the Build tab for your C# project you will see a "Generate serialization assembly" option with a badly aligned-combo box and a mysterious default option of "Auto" that is not described in the MSDN help - more attention to detail by Microsoft in VS2005.


Switching this combo to "On" and compiling a bunch of different projects I found each time that no xml serializer assembly was being produced. This had me rather irritated, and I know of others who had the same problem but no mention of why.

By looking at the MSBuild output with verbose detail you see a first hint as to what is going on:

Assembly 'C:\Foo\obj\Debug\Foo.dll' does not contain any types that can be serialized using XmlSerializer. Please use /verbose switch for more information.

How come? I can run sgen.exe from the command line on the same assembly and that works just fine producing output. Ok, lets take the command from the MSBuild output for sgen.exe and add /verbose to it...

Assembly 'C:\Foo\obj\Debug\Foo.dll' does not contain any types that can be serialized using XmlSerializer. Please use /verbose switch for more information.

WTF? I did add /verbose - fat lot of good it did me. What else is on that sgen.exe command line...

...sgen.exe /assembly:C:\Foo\obj\Debug\Foo.dll /proxytypes /reference:...

Aha - that /proxytypes option looks a bit interesting. The description in the MSDN documentation is "Generates serialization code only for the XML Web service proxy types."

An explanation at last - the property page option is effectively useless unless you are using web services. If you have other uses of xml serialization like many of us do then you have to resort to post-build tasks or scripting sgen.exe externally. All Microsoft had to do was change the combobox options to something like "Off", "ProxyTypes", "All" and this feature would have been far more useful.

Incidentally I also found if you remove the /proxytypes option the /verbose option starts working - quality.

[Update: 17th March 2006]
I should have posted the actual workaround I implemented, which I will now for at least my own purposes of reusing it. By adding the below extract into your .csproj file this will ensure that every time you compile the xml serialization assembly is regenerated for non web proxy classes.

<!-- Grant - this bodge is necessary as Microsoft screwed up the xml serializer
generation by limiting it to only web proxy classes.
The one line change from the equivalent
target in Microsoft.Common.targets is to set UseProxyTypes to false.
-->
<Target Name="GenerateSerializationAssembliesForAllTypes"
DependsOnTargets="AssignTargetPaths;Compile;ResolveKeySource"
Inputs="$(MSBuildAllProjects);@(IntermediateAssembly)"
Outputs="$(OutputPath)$(_SGenDllName)">
<SGen BuildAssemblyName="$(TargetFileName)"
BuildAssemblyPath="$(OutputPath)" References="@(ReferencePath)"
ShouldGenerateSerializer="true" UseProxyTypes="false"
KeyContainer="$(KeyContainerName)" KeyFile="$(KeyOriginatorFile)"
DelaySign="$(DelaySign)" ToolPath="$(SGenToolPath)">
<Output TaskParameter="SerializationAssembly"
ItemName="SerializationAssembly" />
</SGen>
</Target>
<!-- <Target Name="BeforeBuild">
</Target> -->
<Target Name="AfterBuild"
DependsOnTargets="GenerateSerializationAssembliesForAllTypes">
</Target>