Using the System.AddIn namespace to facilitate extensibility, Part Two
Part two of this multi-part series on the System.AddIn model focuses on the add-in pipeline. If you haven't read Part One, or you're not familiar with the System.AddIn model, you should read it before continuing.
Pipeline Overview
The add-in pipeline is a set of logical segments that the host and add in use to communicate with one another. I showed this diagram found on MSDN in Part One and here it is again. It's a good depiction of the pipeline and how each segment relates to another.
I'll describe each segment starting from the left (the host). Note that each segment is usually in its own .Net project. There are some optional exceptions, as I will detail later in this post, but for now just assume they are each a separate entity. Some types in the segments are required to be decorated with attributes found in the System.AddIn namespace. Those types are identified with the appropriate attribute type in square brackets ([example]) in the heading of each description.
Host
The host is the application that utilizes the add in to extend it's own functionality. It can be a Windows application, service, or another add in. It's responsible for discovering, activating, and managing the supported add ins.
Host view of add-ins
Although most of the MSDN documentation describes the host and add-in views as being abstract base classes, they can also be interfaces. Personally, I prefer the interface option, but your needs and preferences will likely vary. The host view represents the signature the host uses to communicate with the add-in.
Host side adapter [System.AddIn.Pipeline.HostAdapterAttribute]
For you design pattern enthusiasts, this is a classic example of the adapter pattern. It's purpose is to convert the types flowing from the host view to the contract segment. It contains no logic other than that necessary to copy a member from the view to the contract.
Contract [System.AddIn.Pipeline.AddInContractAttribute]
As it's name implies, this segment is the contract defined and agreed upon between the host and add-in. It must be an interface and it must implement the IContract interface itself.
Add-In side adapter [System.AddIn.Pipeline.AddInAdapterAttribute]
Just as it's host side counterpart converts the types flowing to the add in from its view, the add-in side adapter converts types from it's view to the contract.
Add-In view [System.AddIn.Pipeline.AddInBaseAttribute]
Again, just a mirror image of the host side view. It can be either an interface or abstract class, and it represents the signature the add-in uses to communicate with the host.
Add-In [System.AddIn.Pipeline.AddInAttribute]
Does this segment really need to be defined? Isn't the add-in the whole reason you're reading this?
Pipeline Development Considerations
You're probably thinking (all two of my loyal readers) of what a pain in the ass pipeline development must be. If you think of all the types you want to pass between the host and the add-in and consider each segment in the pipeline, you're looking at a lot of projects and a lot of plumbing. While that's true, there is some hope. Microsoft graced us with a Visual Studio add-in called the Pipeline Builder. You simply build the contract, and let the tool generate the additional projects for you. Huge time saver. When I started using the System.AddIn model, I didn't know about the tool. I wrote all the plumbing by hand and it was really daunting. Once I stumbled across the tool, however, it eased my pain considerably. I just wish they would have linked to the tool from the MSDN documentation so I would have found it sooner.
If you encounter a situation where you need to pass types that are not serializable between host and add-in, you'll need to take some additional steps. Types like these and collections of non-serializable types, need to have a full pipeline defined for it. You'll have to create a contract, views for both sides, and adapters. Again, with the help of the Pipeline Builder, it won't be too bad.
References
The following segments need the following references:
- Contract: System.AddIn.dll, System.AddIn.Contract.dll
- Add-in view: System.AddIn.dll
- Add-in side adapter: System.AddIn.dll, System.AddIn.Contract.dll, Add-in view segment, Contract segment
- Host side adapter: System.AddIn.dll, System.AddIn.Contract.dll, Host view segment, Contract segment
- Host: System.AddIn.dll, Host view segment
- Add-In: System.AddIn.dll, Add-in view segment
A note about the pipeline, and a very important one, is that when you set up references between the various segments, set the reference's "Copy Local" property to 'false'. If you don't, you're going to run into conflicts and it may take a while to figure out what went wrong. Such conflicts are not immediately obvious.
Directory structure
Your host will have to have a very specific directory structure defined for the add-ins. This very rigid structure is necessary for the framework to discover the add-ins and the various pipeline segment assemblies. The following diagram from MSDN shows what the structure should look like.
Earlier in the post, I mentioned some exceptions to the one project per segment rule. The host views of the add-in and the add-in views of the host can be combined into one assembly. The adapters can also be combined into one assembly, although this is only possible if you also combine the views into their own assembly. If you combine any of the mentioned types into their own assemblies, you have to deploy the assemblies to both sides of the pipeline. I have just been creating every segment in its own assembly so I don't have to remember these rules. Troubleshooting pipeline configuration can be tricky, so I try to keep it as simple as possible.
Closing
In the next part of this series, I'll demonstrate how to build the pipeline and alternatively, how to generate it automatically from a contract.