【问题标题】:InvalidCastException for two Objects of the same type相同类型的两个对象的 InvalidCastException
【发布时间】:2011-01-30 19:30:36
【问题描述】:

我有一个我自己无法解决的奇怪问题。我的 mvp 项目模型中设计为单例的类导致 InvalidCastException

错误的来源可以在此代码行中找到,其中将反序列化的对象分配给类的实例变量:engineObject = (ENGINE)xSerializer.Deserialize(str);。每当我尝试将我的一个用户控件添加到表单或不同的 UC 时,都会发生这种情况。我所有的 UC 都有一个特殊的 Presenter,它可以访问上面提到的单例类的实例变量。

这是我尝试在某处添加 UC 时得到的结果:

'System.TypeInitializationException: The type initializer for 'MVP.Model.EngineData' threw an exception. ----> 

System.InvalidCastException: [A]Engine cannot be cast to [B]Engine. Type A originates from 'MVP.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadNeither' 
   at location '[...]\AppData\Roaming\Microsoft\VisualStudio\9.0\ProjectAssemblies\uankw1hh01\MVP.Model.dll'. 
Type B originates from 'MVP.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadNeither' 
   at location '[...]\AppData\Roaming\Microsoft\VisualStudio\9.0\ProjectAssemblies\u_hge2de01\MVP.Model.dll'
...

所以我不知何故有两个程序集,它们不是从我的项目文件夹访问的,而是从 VS 临时文件夹访问的?我用谷歌搜索了很多,只发现了这个:IronPython Exception: [A]Person cannot be cast to [B]Person。提供了一个解决方案,但它涉及 IronPhyton,我不知道在我的项目中在哪里使用它。

【问题讨论】:

标签: c# exception user-controls assemblies mvp


【解决方案1】:

类型是每个程序集的;如果您将“相同”程序集加载了两次,则程序集的每个“副本”中的类型都不会被视为相同类型。

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

Difference between LoadFile and LoadFrom with .NET Assemblies?

以及 suzcook 博客的链接,了解有关该问题的详细信息。

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

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

【讨论】:

  • 嘿埃里克,首先:谢谢!你是第一个在这个案子上给我任何进一步提示和帮助的人。我已经匆匆浏览了这些文章(并将再次阅读它们),但我已经有一个问题:我没有自己在代码中加载任何程序集。我只是引用项目,然后在设计师的帮助下将 UC 添加到不同的 UC 或 Form。所以我的问题是,当 IDE 负责所有加载过程时,我应该从哪里开始寻找此类加载失败?
  • 我尝试使用融合日志查看器,但什么也看不到(很可能是因为我没有正确使用它)。除此之外,这个工具又有多大帮助的问题是我的失败发生在运行之前?
【解决方案2】:

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

我的回答不会解释如何做到这一点的诡计 - 但我之所以提到它,是因为这个过程直接导致了原始发布者遇到的完全相同的错误。正如 Eric Lippert 所提到的,类型是每个程序集的。因此,如果您多次加载单个程序集,同一个定义的类将显示为不同的类 - 即使目视检查表明它们看起来是相同的。

我们已经看到 .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;
    }

【讨论】:

  • 您的解决方案帮助我发现我不止一次加载程序集并获得“LoadNeither”InvalidCastException 异常。非常感谢。
【解决方案3】:

我的情况涉及同一个 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);

隐藏在其中的是以下内部异常(这与 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 中),错误就消失了。

【讨论】:

    【解决方案4】:

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

    【讨论】:

    • 太生气了,我花了 2 个小时才 google 这个,我不必浪费调试和故障排除来找到触发此异常的代码行。一旦我找到了这行代码,它就会抛出一个异常而没有被 Try/Catch 捕获……太困惑了。很高兴我找到了你的答案。谢谢@M-Kay
    【解决方案5】:

    当我尝试改变这个演员表时,我得到了它:

    var  t = (TeacherWebPages)Session["TeachersAD"];
    

    到这里:

    var  t = Session["TeachersAD"] as TeacherWebPages;
    

    但是程序集/会话/memcache 不同,没有数据返回,但没有发生错误。所以后来,每次源页面从它抱怨的文件夹中更改时,我仍然必须删除特定的临时文件,这需要我从任务管理器中终止 IIS 进程。或者在我的情况下,我可以直接注销并清除会话状态。

    【讨论】:

      猜你喜欢
      • 2013-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-29
      相关资源
      最近更新 更多