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?