Home » c# » Generating an Xml Serialization assembly as part of my build

Generating an Xml Serialization assembly as part of my build

Posted by: admin November 30, 2017 Leave a comment

Questions:

This code produces a FileNotFoundException, but ultimately runs without issue:

void ReadXml()
{
    XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
    //...
}

Here is the exception:


A first chance exception of type ‘System.IO.FileNotFoundException’ occurred in mscorlib.dll

Additional information: Could not load file or assembly ‘MyAssembly.XmlSerializers, Version=1.4.3190.15950, Culture=neutral, PublicKeyToken=null’ or one of its dependencies. The system cannot find the file specified.


It appears that the framework automatically generates the serialization assembly if it isn’t found. I can generate it manually using sgen.exe, which alleviates the exception.

How do I get visual studio to generate the XML Serialization assembly automatically?


Update: The Generate Serialization Assembly: On setting doesn’t appear to do anything.

Answers:

This is how I managed to do it by modifying the MSBUILD script in my .CSPROJ file:

First, open your .CSPROJ file as a file rather than as a project. Scroll to the bottom of the file until you find this commented out code, just before the close of the Project tag:

<!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->

Now we just insert our own AfterBuild target to delete any existing XmlSerializer and SGen our own, like so:

<Target Name="AfterBuild" DependsOnTargets="AssignTargetPaths;Compile;ResolveKeySource" Inputs="$(MSBuildAllProjects);@(IntermediateAssembly)" Outputs="$(OutputPath)$(_SGenDllName)">
   <!-- Delete the file because I can't figure out how to force the SGen task. -->
   <Delete
     Files="$(TargetDir)$(TargetName).XmlSerializers.dll"
     ContinueOnError="true" />
   <SGen
     BuildAssemblyName="$(TargetFileName)"
     BuildAssemblyPath="$(OutputPath)"
     References="@(ReferencePath)"
     ShouldGenerateSerializer="true"
     UseProxyTypes="false"
     KeyContainer="$(KeyContainerName)"
     KeyFile="$(KeyOriginatorFile)"
     DelaySign="$(DelaySign)"
     ToolPath="$(TargetFrameworkSDKToolsDirectory)"
     Platform="$(Platform)">
      <Output
       TaskParameter="SerializationAssembly"
       ItemName="SerializationAssembly" />
   </SGen>
</Target>

That works for me.

Questions:
Answers:

As Martin has explained in his answer, turning on generation of the serialization assembly through the project properties is not enough because the SGen task is adding the /proxytypes switch to the sgen.exe command line.

Microsoft has a documented MSBuild property which allows you to disable the /proxytypes switch and causes the SGen Task to generate the serialization assemblies even if there are no proxy types in the assembly.

SGenUseProxyTypes

A boolean value that indicates whether proxy types
should be generated by SGen.exe. The SGen target uses this property to
set the UseProxyTypes flag. This property defaults to true, and there
is no UI to change this. To generate the serialization assembly for
non-webservice types, add this property to the project file and set it
to false before importing the Microsoft.Common.Targets or the
C#/VB.targets

As the documentation suggests you must modify your project file by hand, but you can add the SGenUseProxyTypes property to your configuration to enable generation. Your project files configuration would end up looking something like this:

  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
    <!-- Snip... -->
    <GenerateSerializationAssemblies>On</GenerateSerializationAssemblies>
    <SGenUseProxyTypes>false</SGenUseProxyTypes>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
    <!-- Snip... -->
    <GenerateSerializationAssemblies>On</GenerateSerializationAssemblies>
    <SGenUseProxyTypes>false</SGenUseProxyTypes>
  </PropertyGroup>

Questions:
Answers:

The other answers to this question have already mentioned the Project Properties->Build->Generate Serialization Assemblies setting but by default this will only generate the assembly if there are “XML Web service proxy types” in the project.

The best way to understand the exact behaviour of Visual Studio is to to examine the GenerateSerializationAssemblies target within the C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727**Microsoft.Common.targets** file.

You can check the result of this build task from the Visual Studio Output window and select Build from the Show output from: drop down box. You should see something along the lines of

C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\bin\sgen.exe /assembly:D:\Temp\LibraryA\obj\Debug\LibraryA.dll /proxytypes /reference:.. /compiler:/delaysign-
LibraryA -> D:\Temp\LibraryA\bin\Debug\LibraryA.dll

The key point here is the /proxytypes switch. You can read about the various switches for the XML Serializer Generator Tool (Sgen.exe)

If you are familiar with MSBuild you could customise the GenerateSerializationAssemblies target so that SGen task has an attribute of UseProxyTypes=”false” instead of true but
then you need to take on board all of the associated responsibility of customising the Visual Studio / MSBuild system. Alternatively you could just extend your build process to call SGen manually without the /proxytypes switch.

If you read the documentation for SGen they are fairly clear that Microsoft wanted to limit the use of this facility. Given the amount of noise on this topic, it’s pretty clear that Microsoft did not do a great job with documenting the Visual Studio experience. There is even a Connect Feedback item for this issue and the response is not great.

Questions:
Answers:

creating a new sgen task definition breaks a fly on the wheel. just set the needed variables to make the task work as intended. Anyway the microsoft documentation lacks some important info.

Steps to pre-generate serialization assemblies

(with parts from http://msdn.microsoft.com/en-us/library/ff798449.aspx)

  1. In Visual Studio 2010, in Solution Explorer, right-click the project for which you want to generate serialization assemblies, and then click Unload Project.
  2. In Solution Explorer, right-click the project for which you want to generate serialization assemblies, and then click Edit .csproj.
  3. In the .csproj file, immediately after the <TargetFrameworkVersion>v?.?</TargetFrameworkVersion> element, add the following elements:

    <SGenUseProxyTypes>false</SGenUseProxyTypes>
    <SGenPlatformTarget>$(Platform)</SGenPlatformTarget>

  4. In the .csproj file, in each platform configuration

    e.g. <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">

    add the following line:

    <GenerateSerializationAssemblies>On</GenerateSerializationAssemblies>

  5. Save and close the .csproj file.

  6. In Solution Explorer, right-click the project you just edited, and then click Reload Project.

This procedure generates an additional assembly named .xmlSerializers.dll in your output folder. You will need to deploy this assembly with your solution.


Explanation

SGen by default only for proxy types generates for “Any CPU”. This happens if you don’t set the according variables in your project file.

SGenPlatformTarget is required to match your PlatformTarget. I tend to think this is a bug in the project template. Why should the sgen target platform differ from your project’s? If it does you will get a runtime exception

0x80131040: The located assembly’s manifest definition does not match the assembly reference

You can locate the msbuild task definition by analyzing your project file:

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

where MSBuildToolsPath depends on your <TargetFrameworkVersion> http://msdn.microsoft.com/en-us/library/bb397428.aspx

Look inside the SGen task definition for TargetFrameworkVersion 4.0 from

Windows installation path\Microsoft.NET\Framework\v4.0.30319\Microsoft.CSharp.targets

to see the undocumented variables like $(SGenPlatformTarget) you are free to set in your project file

<Target
    Name="GenerateSerializationAssemblies"
    Condition="'$(_SGenGenerateSerializationAssembliesConfig)' == 'On' or ('@(WebReferenceUrl)'!='' and '$(_SGenGenerateSerializationAssembliesConfig)' == 'Auto')"
    DependsOnTargets="AssignTargetPaths;Compile;ResolveKeySource"
    Inputs="$(MSBuildAllProjects);@(IntermediateAssembly)"
    Outputs="$(IntermediateOutputPath)$(_SGenDllName)">

    <SGen
        BuildAssemblyName="$(TargetFileName)"
        BuildAssemblyPath="$(IntermediateOutputPath)"
        References="@(ReferencePath)"
        ShouldGenerateSerializer="$(SGenShouldGenerateSerializer)"
        UseProxyTypes="$(SGenUseProxyTypes)"
        KeyContainer="$(KeyContainerName)"
        KeyFile="$(KeyOriginatorFile)"
        DelaySign="$(DelaySign)"
        ToolPath="$(SGenToolPath)"
        SdkToolsPath="$(TargetFrameworkSDKToolsDirectory)"
        EnvironmentVariables="$(SGenEnvironment)"
        SerializationAssembly="$(IntermediateOutputPath)$(_SGenDllName)"
        Platform="$(SGenPlatformTarget)"
        Types="$(SGenSerializationTypes)">
            <Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly"/>
    </SGen>
</Target>

Questions:
Answers:

In case someone else runs into this problem suddenly after everything was working fine before: For me it had to do with the “Enable Just My Code (Managed Only)” checkbox being unchecked in the options menu (Options -> Debugging) (which was automatically switched off after installing .NET Reflector).

EDIT:
Which is to say, of course, that this exception was happening before, but when “enable just my code” is off, the debugging assistant (if enabled), will stop at this point when thrown.

Questions:
Answers:

I’m a little late to the party, but I found the previous answer difficult to work with. Specifically Visual Studio would crash whenever I tried to view the properties of my project. I figure this was due to the fact that it no longer understood how to read the csproj file. That said…

Add the following to your post-build event command line:

"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\sgen.exe" "$(TargetPath)" /force

This will leverage sgen.exe directly to rebuild the Xml Serialization assembly every time you build your project for Debug or Release.

Questions:
Answers:

Look in the properties on the solution. On the build tab at the bottom there is a dropdown called “Generate Serialization assembly”

Questions:
Answers:

A slightly different solution from the one provided by brain backup could be to directly specify the platform target right where you have to use it like so:

<!-- Check the platform target value and if present use that for a correct *.XmlSerializer.dll platform setup (default is MSIL)-->
<PropertyGroup Condition=" '$(PlatformTarget)'=='' ">
  <SGenPlatform>$(Platform)</SGenPlatform>
</PropertyGroup>
<PropertyGroup Condition=" '$(PlatformTarget)'!='' ">
  <SGenPlatform>$(PlatformTarget)</SGenPlatform>
</PropertyGroup>

<!-- Delete the file because I can't figure out how to force the SGen task. -->
<Delete Files="$(TargetDir)$(TargetName).XmlSerializers.dll" ContinueOnError="true" />
<SGen
  BuildAssemblyName="$(TargetFileName)"
  BuildAssemblyPath="$(OutputPath)"
  References="@(ReferencePath)"
  ShouldGenerateSerializer="true"
  UseProxyTypes="false"
  KeyContainer="$(KeyContainerName)"
  KeyFile="$(KeyOriginatorFile)"
  DelaySign="$(DelaySign)"
  ToolPath="$(SGenToolPath)"
  SdkToolsPath="$(TargetFrameworkSDKToolsDirectory)"
  EnvironmentVariables="$(SGenEnvironment)"
  Platform="$(SGenPlatform)">
  <Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly" />
</SGen>