同一类型的两个对象出现 InvalidCastException 异常

26

我有一个奇怪的问题,自己无法解决。我的MVP项目中的一个类被设计成单例模式,却导致了一个InvalidCastException异常。

错误的源头在于这段代码,其中反序列化对象被赋值给该类的实例变量:engineObject = (ENGINE)xSerializer.Deserialize(str);。每当我尝试将我的UserControl之一添加到Form或不同的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 异常:[A]Person 无法转换为 [B]Person。有一个解决方案,但它涉及 IronPhyton,我不知道在我的项目中该在哪里使用它。


请参见https://dev59.com/R3_aa4cB1Zd3GeqPxgTt#23255893。 - Matt Smith
5个回答

21

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

8
根据程序集加载的上下文(上下文为“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;
    }

1
你的解决方案帮助我发现我加载了一个程序集超过一次,并得到了“LoadNeither” InvalidCastException异常。非常感谢。 - Alexei Bondarev

5

我的情况涉及到同一个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中),错误就消失了。


2

我的情况是,Web应用程序引用的类库被重命名并重新构建。旧版本的库仍然以旧名称存储在bin文件夹中。框架会加载bin文件夹中的所有内容(包括这两个库),并发出此错误。因此,当显式加载程序集时,也可能会发生这种情况。 在我这种情况下,明显的解决方案是清理bin文件夹。


真生气,我在调试和排除故障上浪费了两个小时,最后才发现是一行代码引发了异常。当我找到这行代码时,它没有被Try/Catch捕获...我非常困惑。很高兴我找到了你的答案。谢谢@M-Kay。 - Paul - Soura Tech LLC

0

当我尝试更改这个转换时,我成功了:

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

变成这样:

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

然而,汇编/会话/内存缓存不同,并没有数据返回,但也没有出现错误。因此,后来我仍需要每次从源文件所在文件夹更改源页面时删除特定的临时文件,这将需要我从任务管理器中结束IIS进程。或者在我的情况下,我可以注销并清除会话状态。


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