两个相同类型的对象的InvalidCastException

我有这个奇怪的问题,我无法处理自己。 设计为singleton的mvp-project模型中的类会导致InvalidCastException。 在此代码行中找到错误源,其中反序列化对象被分配给类的实例变量: engineObject = (ENGINE)xSerializer.Deserialize(str); 每当我尝试将一个UserControl添加到表单或不同的UC时,就会发生这种情况。 我所有的UC都有一个特殊的演示者,可以访问上面提到的单例类的实例变量。 这是我在尝试在某处添加UC时得到的结果:

‘System.TypeInitializationException:’MVP.Model.EngineData’的类型初始值设定项引发了exception。 —-> System.InvalidCastException:[A]引擎无法转换为[B]引擎。 类型A源自位于'[…] \ AppData \ Roaming \ Microsoft \ VisualStudio \ 9.0 \ ProjectAssemblies \的上下文’LoadNeither’中的’MVP.Model,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null’ uankw1hh01 \ MVP.Model.dll”。 类型B源自位于'[…] \ AppData \ Roaming \ Microsoft \ VisualStudio \ 9.0 \ ProjectAssemblies \的上下文’LoadNeither’中的’MVP.Model,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null’ u_hge2de01 \ MVP.Model.dll” …

所以我不知何故有两个程序集,它们不是从我的项目文件夹访问,而是从VS临时文件夹访问? 我google了很多,只发现了这个: IronPythonexception:[A]人不能被投射到[B]人 。 提供了一个解决方案,但首先它涉及IronPhyton,其次我不知道在我的项目中何处使用它?

如果你能在这里帮助我,那将是很棒的:-) thx

类型是按assembly; 如果你有两次加载“相同”的程序集,那么程序集的每个“副本”中的类型不被认为是相同的类型。

当两个程序集位于Load和LoadFrom上下文中时,通常会出现这些问题。 看到

LoadFile和LoadFrom与.NET程序集之间的区别?

以及有关该问题详情的链接到suzcook的博客。

另外,请考虑使用融合日志查看器来帮助诊断问题。

http://msdn.microsoft.com/en-us/library/e74a18c4%28VS.71%29.aspx

我的特殊情况 – Web应用程序中引用的类库已重命名和重建。 旧版本的库仍然在旧名称下的bin文件夹中。 框架加载bin文件夹(两个库)中的任何内容并发出此错误。 所以它不仅发生在显式加载程序集时。 在我的情况下,明显的解决方案是清理bin文件夹。

我的情况涉及同一个dll的两个副本。 一个在bin文件夹中,一个在同一bin文件夹的子文件夹中。 两者都被加载了,令人惊讶的是有些东西工作得很好,但是有些东西没有,这就是出现这个错误信息的时候:

 System.InvalidOperationException; There was an error generating the XML document.; Source: System.Xml; TargetSite: Void Serialize(System.Xml.XmlWriter, System.Object, System.Xml.Serialization.XmlSerializerNamespaces, System.String, System.String); 

隐藏在此是以下内部exception(这与Microsoft Dynamics CRM 4.0有关,但可能与任何事情有关)

 System.InvalidCastException; [A]XXX.CRMCustomCode.YYY.CreateCompanyRequest cannot be cast to [B]XXX.CRMCustomCode.YYY.CreateCompanyRequest. Type A originates from 'XXX.CRMCustomCode, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadFrom' at location 'C:\Program Files\Microsoft CRM\Server\bin\assembly\XXX.CRMCustomCode.dll'. Type B originates from 'XXX.CRMCustomCode, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location 'C:\Program Files\Microsoft CRM\Server\bin\XXX.CRMCustomCode.dll'.; 

我只是删除了重复的dll(在C:\ Program Files \ Microsoft CRM \ Server \ bin中),错误就消失了。

根据程序集加载的上下文(上下文是“LoadNeither”)判断,一些开发人员可能正在做一些事情,比如加载已经内部打包为应用程序资源的程序集。 如果这样做,您将使用AppDomain.CurrentDomain.AssemblyResolve事件处理程序,以便您的应用程序可以指定.NET应该在何处获得所需的任何特定程序集。

我的答案不会解释如何做到的阴谋 – 但我提到它是因为这个过程直接导致原始海报遇到的同样的错误。 正如Eric Lippert所提到的,类型是按assembly。 因此,如果您多次加载单个程序集,则相同的已定义类将显示为不同的类 – 即使可视检查显示它们看起来相同。

我们已经看到.NET将为同一个DLL多次调用ResolveEventHandler的实例。 我不确定为什么.NET有时会这样做(它发生在某些机器上,但不是所有机器上)。 但是要解决这个问题,我们需要为加载的程序集保留一个全局句柄列表,这样如果.NET想要再次加载程序集,我们会返回最初加载的同一程序集的句柄,而不是将另一个副本加载到内存中。

我已经为我们提供了导致问题的代码,并说明了如何正确处理它。

  public void AppStartup (object sender, StartupEventArgs e) { AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); } public System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll", ""); dllName = dllName.Replace(".", "_"); if (dllName.EndsWith("_resources")) return null; System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly()); byte[] bytes = null; try { bytes = (byte[])rm.GetObject(dllName); } catch (Exception ex) { } if (bytes != null) { // the following call will return a newly loaded assembly // every time it is called // if this function is called more than once for the same // assembly, you'll load more than one copy into memory // this can cause the InvalidCastException // instead of doing this, you keep a global list of loaded // assemblies, and return the previously loaded assembly // handle, instead of loading it again return System.Reflection.Assembly.Load(bytes); } return null; }