Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is XmlSerializer.Deserialize throwing a System.IO.FileLoadException?

I'm having a problem with XML deserialization that is baffling me.

I'm building an application that supports local customization of various services that it uses. I've implemented an abstract ServiceLocator class whose methods return various objects. Each custom installation is responsible for implementing a subclass of this and providing implementations of those methods. The meat of this class looks like this:

public abstract class ServiceLocator
{
    public static void Initialize(string customFeaturesPath)
    {
        Assembly a = Assembly.LoadFrom(customFeaturesPath);
        Type t = a.GetExportedTypes()
            .AsEnumerable()
            .Where(x => x.IsSubclassOf(typeof (ServiceLocator)))
            .First();
        Default = (ServiceLocator)a.CreateInstance(t.FullName);
    }

    public static ServiceLocator Default { get; private set; }

    public abstract DefaultValuesContainer CreateDefaultValuesContainer();
}

This works just fine: I get the path to the custom features assembly from the application configuration file, the program calls Initialize, and then the application can call the various methods on ServiceLocator.Default and they return the appropriate custom implementations of the services.

One of these services is a DefaultValuesContainer. This is a simple object that exposes properties whose values need to be persisted in a user settings file. The idea is that I can serialize this object into a single user setting of type string. It makes for a user setting file that you wouldn't want to edit manually, but I'm cool with that.

Here's a concrete implementation of ServiceLocator.CreateDefaultValuesContainer:

protected override DefaultValuesContainer CreateDefaultValuesContainer(string serializedXml)
{
    DefaultValuesContainer c = new ClientDefaultValuesContainer();

    if (string.IsNullOrEmpty(serializedXml))
    {
        return c;
    }
    XmlSerializer x = new XmlSerializer(c.GetType());
    return (DefaultValuesContainer) x.Deserialize(new StringReader(serializedXml));
}

Now here's the thing.

I've built unit tests for this using NUnit. When I run the tests in the test fixture class that exercises the client custom features, they work. When I run the entire test suite, the last line of the above method throws this exception:

System.InvalidOperationException : There is an error in XML document (0, 0).
  ----> System.IO.FileLoadException : Could not load file or assembly 'ClientCustomFeatures, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. Invalid pointer (Exception from HRESULT: 0x80004003 (E_POINTER))
  ----> System.ArgumentNullException : Value cannot be null.
Parameter name: path1

I'm kind of baffled as to why. The SetUp method still runs, and ServiceLocator.Default still returns an object of type ClientServiceLocator, which means that it has loaded the ClientCustomFeatures assembly. Indeed, the very method that's throwing the exception is in the assembly that I'm being told can't be loaded.

What is the XmlSerializer trying to do here? Why is it trying to load an assembly that's already loaded? What on earth does "Invalid pointer" mean? And above all, how should I be debugging something like this?

like image 366
Robert Rossney Avatar asked Sep 26 '10 22:09

Robert Rossney


1 Answers

If your custom assembly does not know where to load the assembly containing the ClientCustomFeatures class, this will happen. This occurs when you've deployed your custom assembly to a location that is not in the path of your main assembly and your main assembly is not in the gac. So if your custom asseblies are loaded from sub directories of your main assembly this should go away. However, if they are located in arbitrary places, you'll have a problem because they need to load your main assembly as they need access to the ClientCustomFeatures type.

like image 158
Kell Avatar answered Oct 04 '22 20:10

Kell