views:

28

answers:

3

Edit: I would like to keep the infrastructure as is, so while the framework ideas are appreciated, please keep your suggestions centered on the context I have provided.


Background

I'm building a web-based application that dynamically loads plugins. Each plugin comes with a manifest file that contains its dll location, namespace, and type.

Right now I'm using System.Reflection.Assembly.LoadFile to load up the dlls based off the locations provided in the manifest files. Then I load the types and so on.

As an Aside: I may wind up changing to System.Reflection.Assembly.LoadFrom since I'll eventually be loading files from outside the bin directory. But if their is a better way (Assembly.Load or something), feel free to add that in as well

Problem

The problem is that Multiple plugins can potentially run off the same dll. So I wind up executing System.Reflection.Assembly.LoadFile("Identical.dll") multiple times.

I have the idea to check if my assembly has already been loaded by iterating through AppDomain.CurrentDomain.GetAssemblies(), but I don't know if that will help with performance (or if it will work period, I haven't tried it).

Also, I can't keep a list of loaded assemblies due to the project's design constraints (though you may argue that it's a poor design: I can't change it, even if I wanted to OR agreed with you... so please don't press the issue....... please..... unless you really feel that strongly about it, I guess you can add that in as a suggestion).

Ultimately my goals are:

  1. Don't ever re-load the same assembly twice.
  2. Performance is key.
A: 

I don't know if it's going to be an option for you, but have you considered using Spring.Net? You specify via configuration which assemblies are loaded at runtime. It basically handles all the reflection/caching for you.

Adam McKee
Thanks for the suggestion, but I don't think that's going to work in the scenario.
Brandon Boone
+1  A: 

Look at MEF, there's a .NET 3.5 version and it is built into .NET 4.0 RTM. Site has good tutorials and is at http://mef.codeplex.com/ Here's a quick example I wrote figuring it out (though I think you would be looking for the [ImportMany] attribute where you get an enumerable of imported types per http://msdn.microsoft.com/en-us/library/dd460648.aspx#further_imports_and_importmany):

namespace MEF_Interface
{
    // Interface to recognize the concrete implementation as
    public interface IMessageWriter
    {
        void WriteMessage();
    }
}

namespace MEF_HelloMessageWriter
{
    // Concrete implementation in another assembly 
    [Export(typeof(IMessageWriter))]
    public class HelloMessageWriter : IMessageWriter
    {
        public void WriteMessage() { Console.WriteLine("Hello!"); }
    }
}

namespace MEF_GoodbyeMessageWriter
{
    // Concrete implementation in another assembly 
    [Export(typeof(IMessageWriter))]
    public class GoodbyeMessageWriter : IMessageWriter
    {
        public void WriteMessage() { Console.WriteLine("Goodbye!"); }
    }
}

namespace MEF_Example
{
    class DIContainer
    {
        [Import]
        public IMessageWriter MessageWriter { get; set; }

        public DIContainer(string directory)
        {
            // all exports in a specified directory. Filtering is also available.
            DirectoryCatalog catalog = new DirectoryCatalog(directory);
            catalog.Refresh();
            var container = new CompositionContainer(catalog);
            container.ComposeParts(this);
        }
    }

    class Program
    {

        static void Main(string[] args)
        {
            string helloMessageWriterPath =
                @"C:\shared\Projects\MEF_Example\MEF_HelloMessageWriter\bin\Debug";

            string goodbyeMessageWriterPath =
                @"C:\shared\Projects\MEF_Example\MEF_GoodbyeMessageWriter\bin\Debug";

            DIContainer diHelloContainer = new DIContainer(helloMessageWriterPath);
            diHelloContainer.MessageWriter.WriteMessage();

            DIContainer diGoodbyeContainer = new DIContainer(goodbyeMessageWriterPath);
            diGoodbyeContainer.MessageWriter.WriteMessage();

            Console.ReadLine();
        }
    }
}
Jimmy Hoffa
This is very cool. If I had time to re-work the application, I might implement this, but much of the infrastructure is already finished and we're more in the refinement stage at this point. Also, we already have other resources starting plugin development so this really isn't an option. Again though, very cool. +1
Brandon Boone
+1  A: 

If you use LoadFrom for all of your calls, it will only load the assembly once per AppDomain. See the MSDN topic and Suzanne Cook's Choosing a Binding Context for more details.

Jeff Sternal