Sunday, December 2, 2007

Extending your application by creating plugins.

CodeBlog would have been done long ago if I did not keep thinking of other idea's to stick into it.

This morning I was wondering on the best way to approach handling custom sidebar widgets, and it struck me that creating a simple plug-in system would not only be fun, but it would allow other people to develop widgets without having to rebuild the inner workings of CodeBlog.

After a half hour or so of tinkering, I came up with the basic concept for developing a CodeBlog plug-in, though there is a lot more to code out.

The basic concept:
  • Each plug-in is stored within its own assembly.
  • A common interface (ICodeBlogPlugin) is used to reference the plug-in.
  • A static factory method is used in CodeBlog to load and instantiate the plug-in.

Each Plug-In is a class library that references CodeBlog.Plugin.dll. This library provides an interface ICodeBlogPlugin that must be implemented by the plug-in class.

For testing, I used a very basic interface, but I will extend this interface to provide all the method signatures needed for an actual plug-in:

    1 public interface ICodeBlogPlugin

    2     {       

    3         string PluginName

    4         {

    5             get;

    6             set;

    7         }

    8 

    9         void Render();

   10     }


This interface is to be implemented by any plug-in class. For example, to test my idea, I created a new class library and added CodeBlog.Plugin as a reference:

    1     public class TestPlugin : ICodeBlogPlugin

    2     {

    3 

    4         private string _pluginName;

    5 

    6         /// <summary>

    7         /// PluginName Property required by ICodeBlogPlugin

    8         /// </summary>

    9         public string PluginName

   10         {

   11             get

   12             {

   13                 return _pluginName;

   14             }

   15             set

   16             {

   17                 _pluginName = value;

   18             }

   19         }

   20 

   21         /// <summary>

   22         /// Default Constructor

   23         /// </summary>

   24         public TestPlugin()

   25         {

   26             PluginName = "TestPlugin!";

   27         }

   28 

   29         /// <summary>

   30         /// Render Method required by ICodeBlogPlugin

   31         /// </summary>

   32         public void Render()

   33         {

   34             HttpContext.Current.Response.Write(PluginName);

   35         }

   36     }


To load the plugin into a class instance in the CodeBlog Core. I also added a static class called PluginFactory into CodeBlog.Plugin:


    1     public static class PluginFactory

    2     {

    3         public static ICodeBlogPlugin LoadPlugin(string pluginName)

    4         {

    5             Type theType = Type.GetType(pluginName);

    6             return (ICodeBlogPlugin)Activator.CreateInstance(theType);

    7         }

    8     }


Of course, in actual production code, there needs to be exception handling implemented, but this was just some quick test code. Eventually, I will write a plugin loader framework and configuration file, but for now, quickly mocking up some code to test load the plugin was trivial:

    1 ICodeBlogPlugin TestPlugin;

    2 TestPlugin = PluginFactory.LoadPlugin("CodeBlog.TestPlugin.TestPluginClass,CodeBlog.TestPlugin");

    3 TestPlugin.Render();


The potential here is great. A developer can implement the ICodeBlogPlugin interface and create a plug-in that will work directly in CodeBlog. A end user will only have to drop the DLL into the /bin directory and add the assembly name to a configuration file and the plug-in will be ready to use.

This is definitly a fun feature that I hope will be leveraged to its full potential.

2 comments:

Anonymous said...

this is pretty much the standard approach i've seen for implementing plugins ... a few extra steps you could add are:

- create an AppNamePluginAttribute to be used by the class implementing the plugin interface..
-use LoadAssembly and iterate through the dlls in your app's dir and have it try and create an instance only of classes that have the above-mentioned attribute... that would make the plugins more "drag'n'droppish", if you wanted to cut out the config file step. a matter of personal preference, really.

thanks for speaking on the topic... i previously had to search for days to find any articles on implementing a plugin framework for .net, it's good to see more posts about it.

Judah Himango said...

Keep in mind .NET 3.5 adds a namespace dedicated to plugins: System.AddIn. They've eased the burden of creating robust, reliable, safe add-ins.