views:

49

answers:

3

I am trying to create a plug-in type archetecure for my project. I would like the ability to load an assembly, get a type that is derived from an abstract base class in my project, instantiate it and load that derived type into the main processing object.

My problem right now is that when I instantiate the object from the reflected assembly, it is always null. I feel that the problem may lie in the fact that the referenced assembly has 3rd party dlls that it is using. Here is the code: the only exception that gets hit is the final one.

static void Main(string[] args)
    {
        string engineFilePath = ConfigurationManager.AppSettings["EngineFilesDirectory"] + "\\" + ConfigurationManager.AppSettings["EngineDllFileName"];
        Assembly asm = Assembly.LoadFile(engineFilePath);

        Type engineType = asm.GetType(ConfigurationManager.AppSettings["EngineType"]);

        if (!engineType.IsSubclassOf(typeof(EngineConcrete)))
        {
            throw new ArgumentException("Engine is not derived from base implimentation.");
        }

        object engineInstance = asm.CreateInstance(engineType.Namespace + "." + engineType);


        if (engineInstance == null)
        {
           //always thrown at this point
            throw new Exception(string.Format("Engine object is null."));
        }

        return;
    }

If i change the instantiation line to "Activator.CreateInstance(engineType)", I receive an error saying that one of the 3rd party dll's being referenced by the reflected assembly can't be found, though they are in the same directory as the .dll being reflected.

There is a public constructor for the type that is being reflected as well. It has no parameters and inherits from the EngineConcrete class.

+1  A: 

This is the bug: engineType.Namespace + "." + engineType — this expression evaluates into "The.Namespace.The.Namespace.Type" instead of "The.Namespace.Type". Either use engineType.FullName or the Activator.CreateInstance class and use the engineType directly.

Update: Note that MSDN says “… or null if typeName is not found.” about Assembly.CreateInstance's return value.

Ondrej Tucny
Solved the initial problem. But still received "FileNotFoundError"
Ryan Bennett
Again, looking into MSDN, `FileNotFoundExceotion` is thrown when “typeName requires a dependent assembly that could not be found.” See http://msdn.microsoft.com/en-us/library/dex1ss7c(v=VS.90).aspx.
Ondrej Tucny
+1  A: 

I think null is what you get when the type can't be found; the namespace name plus the type name may not be sufficient (strong naming issues). What happens if you put the 3rd-party dlls in the directory of the executing application, not the directory of the plugin?

twon33
Solved the final piece. Thank you
Ryan Bennett
A: 

The issue is that the CLR is not finding the third-party assemblies because they are not in a folder that is being probed.

You could add an event handler for the AppDomain.CurrentDomain.AssemblyResolve event to handle those cases. Below is an example implementation that worked for me where assemblies are being dynamically loaded from a "Plugins" folder that is contained within the bin folder:

class Program
{
    static string _PluginDirectory;
    static string PluginDirectory
    {
        get
        {
            if (_PluginDirectory == null)
            {
                _PluginDirectory = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), @"Plugins");
            }
            return _PluginDirectory;
        }
    }

    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        AssemblyName assemblyName = new AssemblyName(args.Name);
        return Assembly.LoadFile(System.IO.Path.Combine(PluginDirectory, assemblyName.Name + ".dll"));
    }

    static void Main(string[] args)
    {
        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

        var asm = Assembly.LoadFile(System.IO.Path.Combine(PluginDirectory, @"Plugin.dll"));

        var transmogrifier = asm.CreateInstance("Plugin.ConcreteTransmogrifier") as Common.Transmogrifier;

        if (transmogrifier != null)
        {
            Console.WriteLine(transmogrifier.Transmogrify("Wowzers!"));
        }
    }
}
Dr. Wily's Apprentice