4

I'm getting a FileLoadException when attempting to deserialize a type using the NetDataContractSerializer:

The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)

This error does not have to do with the serializer specifically; attempting to load a type at runtime by its Assembly Qualified Name results in the same failure.

I've attached a listener to the AssemblyResolve event to see what's happening:

ResolveEventHandler reh = (o, e) =>
{
    var tryGet = AppDomain.CurrentDomain.GetAssemblies()
                          .Where(x => x.FullName == e.Name).FirstOrDefault();
    if (tryGet != null)
        return tryGet;
    //EDIT:  Crap, the following line is a stupid bug STUPID!  Ignore!
    return Type.GetType(e.Name).Assembly;
};

using (var stream = System.IO.File.OpenRead(serializedObjectFilename))
{
    try
    {
        AppDomain.CurrentDomain.AssemblyResolve += reh;
        var ser = new NetDataContractSerializer();
        return ser.Deserialize(stream) as MyType;
    }
    finally
    {
        AppDomain.CurrentDomain.AssemblyResolve -= reh;
    }
}

The titular "odd behavior" can be seen by debugging through the handler. While tryGet is never null (the required assembly, in this case, is always loaded into the AppDomain), the operation always fails if left to itself. In other words, calling Type.GetType(e.Name).Assembly would result in the FileLoadException being thrown. Edit: I was conflating the strong name of the assembly with the assembly qualified name of a type; please ignore that bug. Ironically, it doesn't throw a different error, so I didn't catch this before asking this question.

Another bit of information: Assembly.Load(e.Name) always returns the valid assembly. I'm not sure why that works whereas the method used behind the scenes during deserialization fails.

Fusion Log reports that the loader is attempting to load the correct assembly, but since the assembly isn't found within the private path of the executable, it fails.

Why is the attempt to load the assembly being made when the assembly is already loaded in the AppDomain??


More details on fusion logging...

I've captured all assembly loading before and during the method call that causes the exception to be thrown. Here are the relevant logs, in order of creation:

  • Partial binding FAILED
    • Attempted to load the assembly by name only
    • Only probed the app base
  • Partial binding via LoadFrom SUCCEEDED
    • Where-ref bind. Location points to where the file is referenced
    • I believe VS uses LoadFrom when loading references in a solution
  • Strong name binding FAILED
    • Unknown what triggered this attempt to load
    • Only probed the app base

AFAICT, Visual Studio loads the assembly into the solution's AppDomain (ffs I wish Fusion Log captured the AppDomain where the load was attempted; it records the calling assembly, after all).

After this point, I make my deserialization call. The result is a single log in Fusion:

Bind result: hr = 0x80070002. The system cannot find the file specified.

Again, Fusion is attempting to load from the executable's application base by its strong name. One good thing; it attempts to load from the GAC, so once deployed I may not have the same issue. But I'm still clueless as to why the assembly cannot be located in the appdomain.


Further interesting stuff...

This throws on the call to Deserialize:

MyType test = new MyType ();
var serialized = Serializer.ToXml(test);
// the following line fails with a FileLoadException
var deserialized = Serializer.FromXml<MyType>(serialized);

where ToXml and FromXml both use the NetDataContractSerializer and Write/ReadObject. The assembly is loaded early in execution from the package's installation directory, but the NDCS, for some reason, doesn't want to use the assembly as it is found in the AppDomain. This test shows that it can't be an issue with versioning.

  • Good question! I'm wondering; if the assembly to load isn't in the path according to the Fusion log, then how did it initially get into the AppDomain's loaded assemblies? How did it resolve once, but not the 2nd time? – CodingWithSpike May 17 '11 at 18:36
  • @rally25rs: Through references. The code is within a VS extension, and the assembly being referenced is also referenced by the open solution. –  May 17 '11 at 18:47
  • The bigger question is why Assembly.Load() works. Log *all* binds with fuslogvw, compare the failed one with the successful one. Post them somewhere for extra eyes. – Hans Passant May 17 '11 at 18:48
  • Assemblies are normally found with Load(). Clearly the assembly is not in the probing path, you made it work with LoadFrom(). Key question is how it got loaded in the first place. Steer very clear of LoadFile(), it has no binding context. You really need to fix your code snippet. – Hans Passant May 17 '11 at 20:36
  • @HansPassant: It was loaded as references are normally loaded. Visual Studio instantiates my Package -> my Package references my assembly -> My assembly is loaded into the AppDomain when types defined within it are used. *I didn't make it work* via LoadFrom; that was Visual Studio loading an assembly which is Referenced by a project. Also, which snippet do you think I should fix? –  May 17 '11 at 20:41
  • @HansPassant: Also, I'm now officially freaking out. NDCS calls `Assembly.Load(string assemblyString)` which is failing; when I call it it succeeds. –  May 17 '11 at 20:50

1 Answers1

0

One thing I find odd in your code is that here:

GetAssemblies().Where(x => x.FullName == e.Name)

e is used as the name of an assembly, since it would match Assembly.Name, so would not have the name of a class/type in it, then here:

return Type.GetType(e.Name).Assembly;

e is used as a fully assembly qualified type name, which I would think would contain both the class/type name plus the assembly name.

Is this intentional?


Edit:

Sorry, I posted this reply just as you edited your post and caught the mistake yourself...

CodingWithSpike
  • 42,906
  • 18
  • 101
  • 138
  • Sorry about that. I would have caught it earlier but `Type.GetType` threw the same FileLoadException. –  May 17 '11 at 18:48