Share via


Debugging an InvalidCastException

First, obviously, find the two types for which the cast failed and verify that they are the same type or otherwise castable.

Next, if the type was just deserialized, also verify that its assembly successfully loaded in the target appdomain.

If everything seems fine, check to see if the assemblies for those two types are loaded from different locations and in the same appdomain. (The actual cast is done in just one appdomain, even if the exception happens when passing a type between two appdomains.) Even if the bits of those assemblies are totally identical, if they are loaded from different paths, they will be considered different, so their types will be considered different. (See Comparing Already-Loaded Assemblies.)

A quick way to check for that is to examine the loaded module window of a debugger to see if that assembly was loaded multiple times. If it was, break on module loads to get the callstack for the unexpected load. If that's inconvenient, try getting the Fusion log.

Usually, the problem is that:

  1. The assembly is available in the GAC (or the ApplicationBase) and loaded there by static reference (something was compiled against that assembly).
  2. It has also been loaded dynamically by path, from another path (LoadFrom(), LoadFile(), etc.).
  3. Then, the code tries to cast the type from (2) to the corresponding type from (1).

To fix this, once you find the offending caller, you will need to either cause the two types to be loaded from the same assembly from the exact same path, or avoid doing the cast. To decide between the assemblies at paths (1) and (2), see Choosing a Binding Context. Usually, I recommend using (1) - see Switching to the Load Context for help with implementing that.

Comments

  • Anonymous
    June 06, 2004
    The comment has been removed

  • Anonymous
    July 21, 2004
    The comment has been removed

  • Anonymous
    July 22, 2004
    The comment has been removed

  • Anonymous
    July 22, 2004
    José: The Unwrap() in CreateInstanceAndUnwrap() may be what's loading the assembly. So, it is not good enough to see that nothing is loaded after that call has finished. Try getting the Fusion log (see original blog entry).

    Another load of the assembly in the calling appdomain is only relevant if the Unwrap()'d type's assembly is cast to it. So, that may be why removing another load did not fix this for you.

    Using a simply-named assembly will not solve this problem if it loaded from two different paths. I recommend that you keep it strongly-named, and avoid the use of LoadFrom()/LoadFile()/Load(byte[]) instead.

    Can't blame this one on VS. :) This is due to the design of remoting - Assembly does not extend MarshalByRefObject, so assemblies are reloaded when passed between appdomain boundaries. Imagine the case where appdomains are on different machines. In that case, it is not convenient or performant to pass the entire file to the remote machine. So, the assembly display name is sent instead, and it is reloaded there. (Having the appdomains on the same machine is not considered a special case.) Additionally, different appdomains have different binding policies which affect what is allowed to be loaded there. So, automatically using an assembly from another appdomain without doing a new bind may not be correct.

  • Anonymous
    July 22, 2004
    The comment has been removed

  • Anonymous
    August 15, 2006
    Thanks to Suzanne Cook for this one. "First, obviously, find the two types for which the cast failed,

  • Anonymous
    August 24, 2006
    The comment has been removed

  • Anonymous
    May 14, 2007
    I created MyCustomSqlMembershipProvider.  I extended the functinality adding some extra methods that I need.  To use them, I'm trying to cast the object as follows: MyCustomSqlMembershipProvider myProvider = (MyCustomSqlMembershipProvider)Membership.Provider; But I get the following error: Unable to cast object of type 'MyProject.App_Code.MyCustomSqlMembershipProvider' to type 'MyProject.App_Code.MyCustomSqlMembershipProvider'. I created a MyCustomSqlMembershipProvider object and compared the Type with the one of Membership.Provider, and they have different Assemblies.  MyCustomProvider is inside App_Code folder of my project.  Here is my web config entry for the membership provider: <membership defaultProvider="MyMembershipProvider" > <providers>    <clear/>    <add name="MyMembershipProvider"             type="MyProject.App_Code.MyCustomSqlMembershipProvider, __code"             connectionStringName="MyConnectionString"/> </providers> </membership> My question are, how to make the cast?  Why VS2005 assigns different Assembly names to each object? Note: [Right now I'm calling the method this way... but I guess that is not the idea behind the provider model: Type type = Membership.Provider.GetType(); object myMembershipProvider = Activator.CreateInstance(type); object[] values = new object[] { Request.UserHostAddress }; bool validUser =(bool) type.InvokeMember("ValidateInNetworkUser", System.Reflection.BindingFlags.InvokeMethod, null, myMembershipProvider , values);]

  • Anonymous
    May 16, 2007
    The comment has been removed

  • Anonymous
    December 28, 2008
    Hi Suzanne & fellow readers, I ran into this issue using the Load(byte[]) context.  As mentioned in the article, each time the assembly was needed to resolve a type (for deserialization, in my case) a new Assembly instance was being created.  This took a few hours to finally figure out because the debugger was telling me that the assemblies were equal, even if the runtime asserts were failing. Anyway, because I needed to cast objects correctly without getting this error, I have overridden this default behavior in the AssemblyResolve handler for my AppDomain by maintaining a Dictionary<string, Assembly>.  When being asked to resolve an assembly, I first check in my dictionary to see if the AssemblyFullName has already been loaded by name, and if it has, return that previously built instance.  (I'm careful to use the full name of the loaded assembly rather than the event argument's request, in case they would ever be different) This solved my immediate problem beautifully, but I'm concerned that the default behavior is the way it is for a good reason!  What kind of mayhem am I creating for myself by overriding this behavior and returning the previously loaded Assembly instance upon subsequent calls? Thanks, J