XmlSerializer没有使用sgen创建的XmlSerializers.dll文件

7
在我的Visual Studio 2010项目中,我使用以下后置生成事件命令行来使用sgen创建XmlSerializers.dll。
后置生成事件:
"$(ProgramFiles)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\sgen.exe" /a:"$(TargetPath)" /c:/keyfile:"c:\myproject\mykey.snk" /f

我的项目是强名称,所以需要使用相同的密钥来对“XmlSerializers.dll”进行强名称。VS会在输出文件夹中创建XmlSerializers.dll。

然而,我通过使用ProcessMonitor发现,在运行时.NET仍会调用CSC.exe。我看到了这篇文章,其中用户遇到了类似的问题,通过使用XmlSerializer(Type)构造函数解决了该问题。

我在代码中使用了相同的技术,但它仍然调用csc.exe:

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

我需要使用预编译XML序列化的原因是出于性能和避免在Windows关闭时出现csc.exe错误的考虑。我的应用程序在窗体关闭时保存数据,但在关闭期间,Windows不允许启动新进程,导致保存失败。我看到有建议通过预编译XML序列化来解决这个问题。

请问为什么XmlSerializer没有使用sgen创建的XmlSerializers.dll呢?

谢谢。


最简单的解决关闭问题的方法是在启动时加载数据。使用fuslogvw.exe检查程序集解析。 - Hans Passant
谢谢,我需要在主窗体关闭时保存数据(序列化为XML)。在用户正常关闭时,没有错误,数据被序列化。但是,如果我的应用程序正在运行并且Windows关闭,则会出现CSC.exe错误。 - Din
3个回答

4
可能问题在于不同的目标平台:默认情况下,sgen使用“任何 CPU”(MSIL),如果包含要进行反序列化或序列化的类型的程序集是针对 x86 或 x64 编译的,则不会加载 .XmlSerializers.dll

更一般地说,我查看了加载序列化程序集的 .NET 代码 - 这里是一些代码,可以重现与单元测试相同的行为:

/// <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

只需调用AssertCanLoadXmlSerializers(),并传递需要序列化/反序列化的类型。如果序列化程序集未加载,则可以通过错误消息相当明确地了解原因。

我将其添加到我们的单元测试中,以便我可以合理地确定序列化程序集是否正常。


这段代码非常有用。谢谢你。我正在尝试理解如果最后一个块的断言失败(“它与汇编ID不匹配”),如何纠正此问题。您能否提供更多说明,以及可能修复它的方法? - Oren Hizkiya
已经有一段时间了...汇编ID由该汇编中模块的版本号组成,如果它们不同,那么意味着这两个汇编(包含类型和序列化的汇编)由不同的模块或相同模块的不同版本组成,因此可能是在创建序列化汇编之后发生了某些更改?查看ID应该可以给你一个提示,说明有什么不同。 - MiMo

0

您确定序列化程序集已正确签名吗?通常需要转义引号。有关更多信息,请参见此处

您还可以检查ID是否匹配。如果在构建序列化程序集后修改源程序集,则ID不再匹配,序列化程序集将不会被使用。有关更多信息,请参见此处

如果所有内容都正确:禁用“工具” -> “选项” -> “调试” -> “启用我的代码”,并启用“调试” -> “异常” -> “公共语言运行时异常” -> “抛出”。然后将应用程序调试到进行序列化的点。将抛出第一次机会异常,说明为什么无法使用序列化程序集。


0
你尝试过在生成的dll上运行ngen.exe吗?
运行中。
ngen.exe install myproject.XmlSerializers.dll

将在图像缓存中安装dll的本机映像,应在运行时使用而不是调用JIT编译器。


谢谢,我已经尝试了您使用ngen.exe的建议,但在ProcessMonitor中仍然看到cse.exe的调用。 - Din
这是ProcessMonitor的屏幕截图:http://screencast.com/t/iyKu79yv"DataSerializer.XmlSerializers.dll"是由sgen创建的序列化程序集。 - Din

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接