Type Extension Metadata

In the previous sections we've seen how a type can be registered as an extension. It is often useful to provide additional metadata in such extensions, so that the host application can get additional information about registered objects even before having to create them. For example, the application could define two metadata properties for the ICommand interface: the label and the icon of the command. By using this metadata, the application could show a menu with the command labels and icons, and this could be done without having to create the ICommand instances.

Metadata can be specified using custom attributes. Extension points can specify a custom attribute type which can be used to define metadata.

Here is an example of how metadata can be defined for the ICommand extension point:

using Mono.Addins;

[TypeExtensionPoint (ExtensionAttributeType=typeof(CommandAttribute)]
public interface ICommand
{
	void Run ();
}

public class CommandAttribute: CustomExtensionAttribute
{
	public CommandAttribute ()
	{
	}

	public CommandAttribute ([NodeAttribute ("Label")] string label)
	{
		Label = label;
	}

	[NodeAttribute]
	public string Label { get; set; }

	[NodeAttribute]
	public string Icon { get; set; }
}

The ExtensionAttributeType property in [TypeExtensionPoint] must be used to specify the custom attribute type that will contain the metadata for the extension point.

The custom attribute type has to follow some basic rules:
  • It must be a subclass of CustomExtensionAttribute.
  • The class must have a default constructor (which doesn't need to be public).
  • There must be a read/write property or a field for each metadata value. This property or field must have the [NodeAttribute] attribute applied to it.
  • If the class has a constructor that sets some properties or fields, each parameter must have a [NodeAttribute (name)] attribute applied to it. The 'name' specified in the attribute must be one of the metadata properties or fields.

Declaring Type Extensions with Metadata

When an type extension point is bound to a custom metadata attribute, that attribute can be directly used to declare extensions, in place of the generic [Extension] attribute. For example:

using System;
using Mono.Addins;

[assembly:Addin]
[assembly:AddinDependency ("HelloWorld", "1.0")]

[Command ("Hello command")]
public class HelloCommand: ICommand
{
	public void Run ()
	{
		Console.WriteLine ("Hello World!");
	}
}

The [Command] attribute (defined above) does two things:
  • Specifies that the HelloCommand class is an extension of the ICommand extension point.
  • Specifies some metadata for that extension. In the example, it sets the Label property to "Hello command".

Querying Metadata

Metadata can be queried using the AddinManager.GetExtensionNodes() method. When an extension point is bound to a custom metadata attribute, the type of the extension node returned by GetExtensionNodes will be TypeExtensionNode<T>, where T is the type of the custom attribute. This type has a Data property which is an instance of the custom attribute declared for the extension object.

The following example shows how metadata can be queried. The application shows a list of registered commands. The list is generated by getting the Label property of each command extension node. Then the application asks the user a number of command to run, and executes it. Notice that the ICommand instance is not created until it has to be executed. That is, it is not necessary to create extension object instances to get their metadata.

using System;
using Mono.Addins;
using System.Collections.Generic;

[assembly:AddinRoot ("HelloWorld", "1.0")]

class MainClass
{
	public static void Main ()
	{
		AddinManager.Initialize ();
		AddinManager.Registry.Update ();

		// Show the list of available commands
		int n = 0;
		Console.WriteLine ("Available commands:");
		ExtensionNodeList commands = AddinManager.GetExtensionNodes (typeof(ICommand));
		foreach (TypeExtensionNode<CommandAttribute> node in commands)
			Console.WriteLine ((n++) + ": " + node.Data.Label);

		// Ask the user which command to run
		Console.Write ("Enter the number of command to run: ");
		int c = int.Parse (Console.ReadLine ()); // Error checks missing
		
		// Create and execute the selected command
		TypeExtensionNode commandNode = (TypeExtensionNode) commands [c];
		ICommand command = (ICommand) commandNode.CreateInstance ();
		command.Run ();
	}
}

Next topic: Data-only Extension Points

Last edited Jul 26, 2010 at 9:35 AM by slluis, version 4

Comments

slluis Jul 26, 2010 at 9:38 AM 
The sample is now fixed. The call should be "AddinManager.GetExtensionNodes (typeof(ICommand))". About setting a path to a custom extension attribute, I'll take a look at this. Thanks!

Montellese Jul 25, 2010 at 2:27 PM 
There is something wrong in the last code. There is a call to AddinManager.GetExtensionNodes<ICommand>() but AddinManager does not provide a parameterless method GetExtensionNodes<>(). Furthermore there seems to be no way to define the extension path when using a custom extension attribute (insertBefore, insertAfter and id are still available).