Let’s start a simple sample of a hosting application that can load calculator add-ins. The add-ins can support different calculation operations that are offered by add-ins.
You need to create a solution with six library projects and one console application. The projects of the sample application are listed in the following table. The table lists the assemblies that need to be referenced. With the references to the other projects within the solution you need to set-the property Copy Local to False, so that the assembly does not get copied. One exception is the HostApp console project that needs a reference to the HostView project. This assembly needs to be copied so it can be found from the host application. Also you need to change the output path of the generated assemblies so that the assemblies are copied to the correct directories of the pipeline.
Let’s start by implementing the contract assembly. Contract assemblies contain a contract interface that defines the protocol for communication between the host and the add-in.
With the following code you can see the contract defined for the calculator sample application. The application defines a contract with the methods GetOperations () and Operate (). GetOperations ()
returns a list of mathematical operations supported by the calculator add-in. An operation is defined by the interface IOperationContract that is a contract by itself. IOperationContract defines the read-only properties Nameand NumberOperands.
The Operate () method invokes the operation within the add-in and requires an operation defined by the IOperation interface and the operands with a double array.
With this contract it is possible that the add-in supports any operations that require any number of double operands and returns one double.
The attribute AddlnContract is used by the AddlnStore to build the cache. The AddlnContract attribute marks the class as an add-in contract interface.
Calculator Add-In View
The add-in view redefines the contract as it is seen by the add-in. The contract defined the interfaces ICalculatorContract and IOperationContract. For this, the add-in view defines the abstract class Calculator and the concrete class Operation.
With Operation there’s not a specific implementation required by every add-in. Instead, the class is already implemented with the add-in view assembly. This class describes an operation for mathematical calculations with the Nameand NumberOperands properties.
The abstract class Calculator defines the methods that need to be implemented by the add-ins. While. the contract defines parameters and return types that need to be passed across appdomain- and process boundaries, that’s not the case with the add-in view. Here you can use types, which make it easy to write add-ins for the add-in developer. The GetOperations () method returns IList<Operation> instead of IListOperation<IOperationContract>, as you’ve seen with the contract assembly.
The AddlnBase attribute identifies the class as an add-in view for the store.
Calculator Add-In Adapter
The add-in adapter maps the contract to the add-in view. This assembly has references to both the contract and the add-in view assemblies. The implementation of the adapter needs to map the method IListContract<IOperationContract> GetOperations () from the contract to the view method IList<Operation> GetOperations ().
The assembly includes the classes OperationViewToContractAddlnAdapter and CalculatorViewToContractAddlnAdapter. These classes implement the interfaces IOperat:ionContract and ICalculatorContract. The methods of the base interface IContract can be implemented by deriving from the base class ContractBase. This class offers a default implementation. OperationViewToContractAddlnAdapter implements the other members of the IOperationContract interface and just forwards the calls to the Operation view that is assigned in the constructor.
The class OperationViewToContractAddlnAdapter also contains static helper methods ViewToContractAdapter () and ContractToViewAdapter () that map Operation to IOperationContract and the other way around.
The class CalculatorViewToContractAddlnAdapte1,” is very similar to, OperationViewToContractAddlnAdapter: It derives from ContractBase to inherit a default implementation of the IContract interface, and it implements a contract interface. This time the ICalculatorcontract interface is implemented with the GetOperations () and Operate () methods.
The Operate () method of the adapter invokes the Operate () method of the view class Calculator where IOperationContract needs to be converted to Operation. This is done with the static helper method ContractToViewAdapter ( ) that is defined with the OperationViewToContractAddlnAdapter class.
The implementation of the GetOperations method needs to convert the collection- IListContract<IOperationContract> to IList<Operation>. For such collection conversions, the class CollectionAdapters defines conversion methods ToIList () and ToIListContract (). Here, the method ToIListContract () is used for the conversion.
The attribute AddlnAdapter identifies the class as an add-in side adapter for the add-in store.
Because the adapter classes are invoked by .NET reflection, it fa pouible that the’ intemalacceu modifier is used with these cluaes. As these claues are an implementation detail, irs a good idea to use the internal acceu modifier.
The add-in now contains the real implementation of the add-in. The add-in is implemented by the class CalculatorVl. The add-in assembly has a dependency on the add-in view assembly as it needs to implement the abstract Calculator class.
The attribute AddIn marks the class as an add-in for the add-in store, and adds publisher, version, and description information. On the host side, this information can be accessed from the AddInToKen.
CalculatorVl returns a list of supported operations in the method GetOperations (). Operate (1 calculates the operands based on the operation.
Calculator Host View
Let’s continue with the host view of the host side. Similar to the add-in view, the host view defines an abstract class with methods similar to the contract, However, the methods defined here are invoked by
the host application.
Both the class Calculator and Operation are abstract as the members are implemented by the host adapter. The classes here just need to define the interface to be used by the host application.
Calculator Host Adapter
The host adapter assembly references the host view and the contract to map the view to the contract. The class OperationContractToViewHostAdapter imp.lements the members of the abstract Operation class. The class CalculatorContractToViewHostAdapter implements the members of.the abstract Calculator class.
With operationContractToViewHostAdapter, the reference to the contract is assigned in the constructor. The adapter class also contains a ContractHandle instance that adds a lifetime reference to
the contract, SO that add-in stays loaded as long it is needed by the hosting application.
The classCalculatorContractToViewHostAdapter implements the methods of the abstracthost view Calculator cIaSs and forwards the callto the contract.Again, you can see the ContractHandle holding the referenceto the contract,which issimilarto the adapter from the add-in side type conversions.This time the type conversions arejustin the other directionfrom the add-in adapters.
The attributeHostAdapter marks the classas an adapter thatneeds tobe installedin the HostSideAdapters directory.
The XAML code that follows shows the tree of the user interface. With the ListBox elements, different styles with its templates are used to give a specific representation of the list of add-ins, the list of
operations, and the list of operands.
In the code behind, the FindAddIns () method is invoked in the constructor of the Wmdow. FindAddlns () uses the AddlnStore class to get a collection of AddlnToken objects and pass them to the DataContext property of the ListBox listAddlns for display. The first parameter of the AddlnStore. FindAddlns () method passes the abstract Calculator class that is defined by the host view to find all add-ins from the store that apply to the contract. The second parameter passes the directory of the pipeline that is read from the application configuration file. When you run the sample application from the Wrox download site you have to change the directory in tpe application configuration file to match your directory structure.
To update the cache of theAdd-In store,the UpdateStore () and RebuildStore () methods are mapped to the Click events of the Update and Rebuild buttons.Within the implementation of these methods, the Rebuild () or Update () methods of the AddlnStore classare used. These methods return a stringarray of warnings ifassemblies are stored in the wrong directories.Because of the complexity of the pipeline structure,there’sa good chance that the firsttime you may not get the projectconfiguration completely rightforcopying the assemblies to the correctdirectories.Reading the returned information from these methods, you willget a clearexplanation about what’s wrong. For example, the message “No usable AddlnAdapter parts could be found in assembly Pipeline\AddInSideAdapters\Ca1cView .dll”gives a hint thatthe assembly Ca1cView isstored insidethe wrong directory.