Wednesday, September 10, 2008

Simple .NET Plugin Architecture

Imagine you have created a tool which writes some stuff into files (no need to know here what it actually does ;) ) and suddenly somebody has the need for another structure in this file.

What do you do?

First you look back at your code and are quite happy about the fact, that you actually can extract all of your writing and reading related methods from such a file into an Interface ;). This Interface can look like

public interface IFileAccess
{
    bool WriteEntry(Entry toWrite);
    Entry ReadEntry();
}

and it will be very useful, if it is created in a new project to let other people build this Plugins too, without seeing the rest of your work.

For this given Interface, you create some other Implementations: just like you need to store the Data. For Example we have now one class XMLFileAccess, one BinFileAccess and one TextFileAccess. For this Implementation it would also make sense to create an new project each: Why? It is simpler to handle several Assemblies than to extract Types from an given Assembly.

What we have now is a Assembly with the Interface and the needed Businessobjects (in our Example the class Entry) and 3 Assemblys with an Implementation of IFileAccess.

To dynamically load this Types in your MainApplication, you just need to do something like this:
public class FileAccess
{
    private static IFileAccess _instance;

    //Static Property to Access Implemenation of IFileAccess
    public static IFileAccess Instance
    {
        get
        {
            if (_instance == null) throw new ArgumentException("FileAccessor not initialized!");
            return _instance;
        }
    }
    //Init the FileAccess
    public static void LoadAssembly(string assembly)
    {
        try
        {
            //remove current instance
            _instance = null;

            Assembly file = Assembly.LoadFile(assembly);

            AppDomain currentDomain = AppDomain.CurrentDomain;

            Type[] types = file.GetTypes();
            foreach (Type type in types)
            {
                if (typeof(IFileAccess).IsAssignableFrom(type))
                {
                    _instance = (IFileAccess)currentDomain.CreateInstanceFromAndUnwrap(assembly, type.FullName);
                    //Take the first found Implementation: do not allow further ones
                    break;
                }
            }
            if (_instance == null)
            {
                throw new ApplicationException("Could not create Instance for FileAccessor!");
            }
        }
        catch (Exception e)
        {
            throw new ApplicationException("Could not create Instance for FileAccessor!", e);
        }
    }
}

The rest is up to you: handling parallel Instances, holding and providing the used Assemblies, setting default Handlers, etc.

Have fun :)

No comments:

Post a Comment