I have a class library (CoreLib) which exposes a bunch of functionality, both out-of-the box and through add-ons.
The interface for interacting with addons is very general (on purpose), where each addon can return metadata describing the properties it exposes. The base class for addons looks something like this:
public abstract class AddOnBase
{
public abstract IList<PropertyDescriptor> GetPropertyDescriptors();
public void SetPropertyValue(Guid propertyId, object value);
public object GetPropertyValue(Guid propertyId);
}
This generality works very nicely for someone building a UI to expose the addon's properties.
However, if someone wants to work with the addon in plain old .NET code, it's way too cumbersome. For example:
// Declare some published IDs to identify the "Recipe" addon and its properties.
Guid recipeAddOnId = new Guid("...");
Guid recipeTitlePropId = new Guid("...");
Guid recipeCategoryPropId = new Guid("...");
AddOn addon = coreLib.GetService(IAddOnService).GetAddOnInstance(recipeAddOnId);
// Set some properties in the recipe addon.
addon.SetPropertyValue(recipeTitlePropId, "Cupcakes");
addon.SetPropertyValue(recipeCategoryPropId, "Baked Goods");
// Do something with the addon.
coreLib.GetService(IAddOnService).ProcessAddOn(addon);
This works fine, but there's all these GUIDs that the developer needs to look up, and it makes everything really clunky.
I would much rather the developer could add a reference to the RecipeAddOn and write something like this instead:
Guid recipeAddOnId = RecipeAddOn.Id;
AddOn addon = coreLib.GetService(IAddOnService).GetAddOnInstance(recipeAddOnId);
// Cast the instance returned by CoreLib to a RecipeAddOn
RecipeAddOn recipe = (RecipeAddOn)addon;
// Set some properties in the recipe addon.
recipe.Title = "Cupcakes";
recipe.Category = "Baked Goods";
// Do something with the addon.
coreLib.GetService(IAddOnService).ProcessAddOn(recipe);
Here, not only do they not have to mess with GUIDs to identify properties, but they get to work with more concrete classes and get intellisense to help them in their development.
My current solution is to have the developer reference both CoreLib and RecipeAddOn, with CopyLocal
set to false
on the latter. However, it's been suggested that this can lead to DLL hell (see Why do I need an AssemblyResolve handler for an assembly which is already loaded?), so I wanted to ask what is the proper way to do this.
My extension setup is always like this. It's from days of C and Pascal where everybody publishes an header file or pascal unit with interface and a lib/obj file to link with.
Client references Interface dll which contains IRecipeAddon
interface. On Runtime client also loads Implementation dll, creates an instance of RecipeAddon
concrete class and casts it to IRecipeAddon
interface. Developer gets intellisense, and add on implementation does not mixed with client code.