One of the demands from a successful application is the abilityto easily adept to the needs of its users if and when they change (and they do). Many times, the required changed, or extra features that need to be added require not only pure development work, but also re-compilation of some or all of the application binaries (this discussion does not include web applications, only winform applications).
To avoid having to go through this sometimes painful process just to add a simple feature, or fix a simple bug, a new need has grown - to allow extending an existing application without the need to recompile it or even without the help of the original developer. This is how what we know today as Add-ins and plugins were born.
When we moved into the world of .Net, we still have the ability to write applications that can be extended by others. However, there is no clear path, a common way, to design and implement such abilities into .Net applications. If you were to go today and try to build an extensible application, you'll be faced with a myriad of articles and HOW-TOs, each explaining the story a little bit differently than it's predecessor.
This Application block was created so that we can start and retain a common ground for how such abilities should be built. So that it can be both easy to understand and powerful enough as to be used by the most demanding applications.
The solution I present here is based on my experience writing extensible applications in .Net and on all the things I learned while trying to figure out the best way to achieve specific abilities while trying to maintain a generic approach.
How does it work?
There are 3 basic principles that need to be implemented in order for an application to have plugins:
1. |
The application needs a way to recognize and communicate with the plugins
|
2. |
The Plugins need a way to talk (or sometimes control) the application in which they are hosted
|
3. |
The plugins need to be recognized and loaded at runtime into the host application
|
1. The application needs a way to recognize and communicate with the plugins
This application block solves this problem by providing a common interface that all plugins must implement:IPlugin.
The principle is actually very simple:
Let's say I have a text editing application to which I would like people to be able to add plugins.
These plugins should be able to talk to the application in which they reside, AND the application should be able to talk to them.
However, since the application does not know in advance what plugins may be available (some of them may only be created in the future!) the application cannot have a direct reference on any specific DLL or class. It needs to support ANY CLASS but it needs to be able to talk to it on pre-known terms.
This paradox is solved by the using a COMMON INTERFACE that both the plug in and the application know. This interface is declared in a separate assembly and both the plug in and the application have a reference to that assembly.
This interface is what our application will use to communicate with the plug in. Any plug in that will want to attach itself to the app will have to implement this interface or otherwise we won't even try to load it. This interface is called (in this application block) IPlugin.
2. |
The Plugins need a way to talk (or sometimes control) the application in which they are hosted
|
This application block solves this problem by providing a base class called AppContext. To the Plugins, AppContext is their one and only link to the outside world. Anything they need to do, anything they need to know, they do it through this class. The host application will hold a class derived from AppContext which has specific methods and properties which should be shared with the plugins. the IPlugin interface contains an Init Method which passes an AppContext instance into the plug in, allowing it to hold a reference to it and using it for what it needs. For example, For an Editor application, We can have an EditorContext class which exposes events such as TextChanged and properties such as SelectedText and so on. The plugins can use these properties to manipulate their outside environment . It all depends on how much the developer decides to expose to the plugins using the AppContext class. The AppContext class is also the class that will load and unload the plugins when the application starts and finishes, but for that it uses yet another class from the application block - the PluginProvider classes which I'll now discuss in the next paragraph.
3. |
The plugins need to be recognized and loaded at runtime into the host application
|
There are many ways in which one can organize and recognize the plugins that their application needs to load. Some prefer to dynamically search all the DLLs in the app directory and find classes that implement the IPlugin Interface. Some prefer to list the required plugins and their type inside the application config file. Some might have a whole different way of doing this which no one has ever thought of before. This application block is built to support whatever method you choose to load your plugins, and it also provides a couple of implementations right off the bat so you can get up and running in no time with your new application.
In the extensibility block there are special objects for which the only purpose in life if to find and load plugins. These are called Plugin Providers. Each plug in provider must implement the IPluginProvider interface. There are several providers already build for you. These include the XmlFilePluginProvider, SectionHandlerPluginProvider and DynamicFindPluginProvider. Each of these implements searching for plugins in a totally different way, but because they all support the same interface, they all return a collection of IPlugin object when they are done allowing you to simply choose the method in which to search.
Don't find anything you like in there? Feel like you need an extra feature? Simple implement the IPluginProvider interface in your own class and you're free to go.
Download EAP 1.0