Using the System.AddIn namespace to facilitate extensibility.
If you've ever written a plug in framework for an application, you can appreciate how much work goes into managing the framework and the add ins it supports. The purpose of this post is to introduce you to the facilities contained in the .Net framework that make add in support much, much easier. First, let me identify some core concepts surrounding add in architectures. I'll try to keep these concepts platform and language neutral, since the need for application extensibility transcends platforms. Then, I'll show you how the classes in the System.AddIn namespace strive to meet them.
Note that when .Net 3.5 was in beta and the news of this add in framework was first released, it was referred to as the Managed AddIn Framework (MAF). Since it's release, however, the CLR team has been referring to it as simply the "System.AddIn model".
General Add In Core Concepts
Add in models require two basic entities: the add in host and the add in itself. While both players are rather obvious, it's important to note that the host can take the form of an application, service, operating system, or even another add in.
The host is responsible for identifying supported add ins, activating them, and managing their lifetime. The add in itself is responsible for supplying additional functionality to it's host.
Discovering available add ins is the first step to being able to use them. In a fully functional architecture, your host must be able to discover add ins and report on them without activating them. This allows you to selectively activate and deactivate add ins as the needs dictate.
In order to allow the add in to perform it's intended purpose, it must be activated. Activation includes creating an instance of the add in and turning over control to it. Your specific needs will dictate what level of isolation is required for your application.
It would be a grave mistake to allow a hosted add in to affect the stability of the host, so isolation is, in my opinion, the most important concept involved in extensibility. It is vital that an add in be isolated from the rest of the application and only allowed to interact in the way the host designer intended.
The ultimate in isolation is running the add in out of process. That is, in it's own process. Due to performance restrictions, however, some architects prefer they are isolated in logical sandboxes or sub processes.
In most modern languages and platforms, the lifetime of objects is handled by the underlying runtime. This is a great benefit to developers, not having to worry about memory management. But in the case of add ins that are isolated from the hosting process, standard lifetime management isn't enough. Referential integrity must be maintained across process boundaries to ensure the references are not prematurely garbage collected.
Now that we've identified some key areas of interest when considering add in architectures, let's see how the System.AddIn namespace strives to resolve them. First, I'll talk just a bit about about a few key classes you'll be using and the concept of the add in pipeline.
System.AddIn.Hosting.AddInStore is a static class that contains methods related to the physical storage and retrieval of add ins.
System.AddIn.Hosting.AddInToken is a class that represents an add in and its associated pipeline segments that can be activated.
The add in pipeline is a term used to refer to the pipeline of communication between the add in and its host. While creating and understanding the various segments can become complex, it's important to note that there is a tool called the Pipeline Builder that be used to generate some portions of the pipeline, making development and maintenance a bit easier.
The following diagram is a popular one to show the various pipeline segments and their relations.
I think the best explanation of each pipeline segment was best done in the MSDN documentation, which can be found here.
As mentioned above, the AddInStore class contains methods that are used to manage the storage and retrieval of add ins. It discovers newly added add ins and maintains a cache summary of the available add ins. In short, discovery is nicely abstracted by the AddInStore methods, as long as you provide the correct base add in directory to the Update method.
Just as discovery was abstracted away for us, so is activation. The AddInTokens we've already discovered contain a generic method aptly named Activate that takes care of that for us. It's at this point that we specify our level of isolation and security by calling the proper overloaded version of said method.
With the System.AddIn model, you have a few isolation options. You can activate an add in in an existing application domain, a new application domain, or an external process. As I mentioned in the core concepts section, your specific needs will dictate which level of isolation you'll choose.
If you're not familiar with what an application domain is, shame on you. It's a fundamental concept of the .Net framework and you should become familiar with it immediately.
As I mentioned under Activation in this section, you simply choose the proper overload of the AddInToken.Activate method that suits your needs. The framework takes care of the rest for you. This is one of the largest areas of benefit the System.AddIn model provides. Previous to this model, you had to manually spawn appdomains and/or processes and manage them yourself. It was a very cumbersome and sometimes tricky process.
The .Net garbage collector is not equipped to manage the lifetime of objects that are used across isolation boundaries. The logic involved in doing this is once again abstracted from us. The responsible classes use a combination of reference counting and remoting services to accomplish this. You have to do nothing to maintain the reference. Should you wish to terminate the reference and the add in instance at the add in side, there is a class called the AddInController that can help. It contains a Shutdown method that will free up references and resources for you.
The System.AddIn model has helped me overcome many extensibility issues and it made it a pleasure doing so. I encourage anyone thinking of designing such an application to spend some time getting to know the model. It will save you a lot of time and a lot of heartache, I'm sure of it. If anyone runs into any limitations they are not able to overcome, I would like to hear about it. So far, it's done everything I've needed it to without a hitch.
The best documentation I've found is on MSDN and this location is a good starting place.