Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XmlSerializer not using XmlSerializers.dll created by sgen

In my Visual Studio 2010 project, I use following Post-Build event command line to use sgen to create XmlSerializers.dll.

Post build event:

"$(ProgramFiles)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\sgen.exe" /a:"$(TargetPath)" /c:/keyfile:"c:\myproject\mykey.snk" /f

My project is strong named, so use the same key to strong name the "XmlSerializers.dll". VS creates the XmlSerializers.dll in output folder.

However, I have noticed using ProcessMonitor, .NET still invoke CSC.exe at runtime. I came accross this post, where the user had similar issue and resolved by using XmlSerializer(Type) constructor.

I used same technique in my code but it still invoke csc.exe:

var fs = new FileStream(SettingsFilePath, FileMode.Open);
try
{
var serializer = new XmlSerializer(typeof(AppSettings));
settings = (AppSettings)serializer.Deserialize(fs);
}
finally
{
fs.Close();
}

The reason I need to use precompiled XML serialisation, because of performance and also I have seen sometimes csc.exe erros on Windows shutdown. My application saves data when Form close, during shutdown, it fails because Windows will not allow a new process to start during the shutdown sequence. I have seen recommendations to get around this by precompiling XML serialisation.

Any suggestions about why XmlSerializer not using XmlSerializers.dll created by sgen?

Thanks.

like image 618
Din Avatar asked May 09 '12 08:05

Din


1 Answers

Possiby the problem is different target platform: by default sgen uses 'Any CPU' (MSIL), if the assembly containing the type to be deserialized or serialized is compiled for x86 o x64, it won't load the .XmlSerializers.dll

More in general, I had a look at the .NET code that load the serialization assemblies - here is some code that reproduce the same behavior as a unit testing:

/// <summary>Generates an identifier for the assembly of a specified type</summary>
/// <remarks>Code copied from the .NET serialization classes - to emulate the same bahavior</remarks>
/// <param name="type">The type</param>
/// <returns>String identifying the type's assembly</returns>
static string GenerateAssemblyId(Type type) 
{ 
  Module[] modules = type.Assembly.GetModules();
  ArrayList list = new ArrayList();
  for (int i = 0; i < modules.Length; i++) {
    list.Add(modules[i].ModuleVersionId.ToString()); 
  }
  list.Sort(); 
  StringBuilder sb = new StringBuilder(); 
  for (int i = 0; i < list.Count; i++) {
    sb.Append(list[i].ToString()); 
    sb.Append(",");
  }
  return sb.ToString();
} // GenerateAssemblyId

/// <summary>Verifies that the serialization assembly for the specified type can be loaded</summary>
/// <remarks>Code copied from the .NET serialization classes - to emulate the same behavior and tests</remarks>
/// <param name="type">The type</param>
static void AssertCanLoadXmlSerializers(Type type)
{
  if (type == null)
    throw new ArgumentNullException("type");
  Assembly serializerAssembly = null;
  // Create the name of the XML serilizers assembly from the type's one
  AssemblyName name = type.Assembly.GetName(true); 
  name.Name = name.Name + ".XmlSerializers"; 
  name.CodeBase = null;
  name.CultureInfo = CultureInfo.InvariantCulture;
  try {
    serializerAssembly = Assembly.Load(name);
  } catch (Exception e) {
    Assert.Fail("Unable to load XML serialization assembly for type '{0}': {1}", type.FullName, e.Message);
  }
  object[] attrs = serializerAssembly.GetCustomAttributes(typeof(XmlSerializerVersionAttribute), false);
  if (attrs == null || attrs.Length == 0) {
    Assert.Fail(
      "Unable to use XML serialization assembly '{1}' for type '{0}': it does not contain XmlSerializerVersionAttribute", 
      type.FullName, 
      serializerAssembly.FullName
    );
  }
  if (attrs.Length > 1) {
    Assert.Fail(
      "Unable to use XML serialization assembly '{1}' for type '{0}': it contains multiple XmlSerializerVersionAttribute", 
      type.FullName, 
      serializerAssembly.FullName
    );
  }
  XmlSerializerVersionAttribute assemblyInfo = (XmlSerializerVersionAttribute)attrs[0];
  string assemblyId = GenerateAssemblyId(type);
  if (assemblyInfo.ParentAssemblyId != assemblyId) {
    Assert.Fail(
      "Unable to use XML serialization assembly '{1}' for type '{0}': it does not match assembly id '{2}'", 
      type.FullName, 
      serializerAssembly.FullName,
      assemblyId
    );
  }
} // AssertCanLoadXmlSerializers

simply call AssertCanLoadXmlSerializers() passing the type than needs to be serialized/deserialized. If the serialization assemblies do not load you can have a fairly good idea of why from the error messages.

I added it to our unit testing, so that I can be reasonably sure that the serialization assemblies are OK.

like image 111
MiMo Avatar answered Oct 04 '22 02:10

MiMo