问题
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.
回答1:
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.
回答2:
Have you tried running ngen.exe on the generated dll?
Running
ngen.exe install myproject.XmlSerializers.dll
will install a native image of the dll in the image cache, which should be used at runtime rather than invoking the JIT compiler.
回答3:
Are you sure that the serialization assembly is correctly signed? Usually you need to escape the quotes. See here for more information.
You could also check if the IDs match. If you modify the source assembly after building the serialization assembly the IDs don't match any more and the serialization assembly won't be used. See here for more information.
If all that is correct: Disable Tools -> Options -> Debugging -> "Enable Just My Code" and enable Debug -> Excpetions -> Common Language Runtime Exceptions -> Thrown. Then debug your application to the point where the serialization is done. An first chance exception will be thrown stating why the serialization assembly can't be used.
来源:https://stackoverflow.com/questions/10512299/xmlserializer-not-using-xmlserializers-dll-created-by-sgen