-
Notifications
You must be signed in to change notification settings - Fork 6
Extending Vault
Vault is built for extensibility. It's built such that additional TypeHandler
s can be easily added, and additional libraries can be included alongside it. With these techniques, you can create additional custom classes and libraries to extend Vault's capabilities for handling types.
This diagram overviews what happens when Vault decides that an umbraco property field needs to be mapped to an object property.
When you wish to map to a data type that isn't an Umbraco object but has a specific class, these primary tasks needed to be performed:
- Create a class that implements
UmbracoVault.TypeHandlers.ITypeHandler
- Register the TypeHandler with the
UmbracoVault.TypeHandlers.TypeHandlerFactory
. - (Optional) Extend
UmbracoPropertyAttribute
to declare a specific type handler to an existing type
Type Handlers have 1 job: Take an Umbraco property value, and map it to a specified object type. Each type handler does this, by implementing this interface:
public interface ITypeHandler
{
object GetAsType<T>(object input);
System.Type TypeSupported { get; }
}
A simple Type Handler that maps a property to a string will look like this:
public class StringTypeHandler : ITypeHandler
{
public object GetAsType<T>(object input)
{
return Get(input.ToString());
}
public Type TypeSupported
{
get { return typeof (string); }
}
}
Lastly, register the type handler with the UmbracoVault.TypeHandlers.TypeHandlerFactory
. This can be done in two ways.
- Explicitly register the type on your application init methods.
- Add the assembly attribute
UmbracoVault.Attributes.ContainsVaultTypeHandlers
to your C# project for auto-registration.
//Global.asax.cs or equivalent
public class Global : UmbracoApplication
{
protected override void OnApplicationStarting(object sender, System.EventArgs e)
{
DefaultRenderMvcControllerResolver
.Current
.SetDefaultControllerType(typeof(VaultDefaultGenericController)); //optional
Vault.RegisterViewModelNamespace("ReferenceWebsite.Models", "ReferenceWebsite");
UmbracoVault.TypeHandlers.TypeHandlerFactory..RegisterTypeHandler<CustomTypeHandler>();
}
}
Vault, on initialization, will automatically search for type handlers to register such that manual registration is not needed. To do this, simply qualify your assembly by putting the assembly attribute somewhere in your class files (Properties/AssemblyInfo.cs
is a great place for this)
//Properties/AssemblyInfo.cs
[assembly: AssemblyTitle("UmbracoVault.Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
...
[assembly: ContainsUmbracoVaultTypeHandlers]
...
All types within your assembly that implement UmbracoVault.TypeHandlers.ITypeHandler
will be auto loaded. It is possible to exclude specific type handlers from being auto loaded by decorating with the property UmbracoVault.Attributes.IgnoreTypeHandlerAutoRegistration
. In order to properly use these type handlers, you will need to extend UmbracoProperty
and explicitly set the TypeHandler
with an instance of your type handler within the attribute. See below for more on this.
//Will not be auto loaded by Vault.
[IgnoreTypeHandlerAutoRegistration]
public class AutoRegisteredTypeHandler : ITypeHandler
{
public object GetAsType<T>(object input)
{
throw new NotImplementedException();
}
public Type TypeSupported { get { return typeof (AutoRegisteredType); } }
}
public class AutoRegisteredType { }
The [UmbracoProperty]
attribute denotes that a property should be mapped from an Umbraco document. it also can contain metadata on the property alias to bring in and also the type handler that should be used to map it.
In some instances, having a type mapper alone won't be enough for Vault to map your property. For example, if you need a special type handler for a common C# type. Let's say you map to an int value but the data is stored as XML.
Consider this Umbraco Document:
<Person id="1077" parentID="1054" level="2" creatorID="0" sortOrder="7" createDate="2014-12-05T15:14:45" updateDate="2014-12-18T14:03:01" nodeName="Ken Sykora" urlName="ken-sykora" path="-1,1054,1077" isDoc="" nodeType="1071" creatorName="admin" writerName="admin" writerID="0" template="0" nodeTypeAlias="Person">
<name>Ken Sykora</name>
<primaryLocation><location><id>1073</id><name<Minneapolis, MN</name></location></primaryLocation>
<secondaryLocation><![CDATA[1074]]></secondaryLocation>
<favoriteLocations><![CDATA[1073,1076]]></favoriteLocations>
<photo>/media/1001/headshot.jpg</photo>
</Person>
and this Object we wish to map to:
public class Person {
public string Name { get; set; }
public int LocationId { get; set; }
}
For the sake of example, let's say we want to mape the Person/primayLocation/location/id
field to the LocationId
property of the C# object. We will need a custom TypeHandler for this, but since there already exists a type handler for int
values, we need to explicitly state that we want this type handler to be used.
public class LocationIdTypeHandler : ITypeHandler
{
public object GetAsType<T>(object input)
{
var value = input.ToString();
int result = 0;
if (!string.IsNullOrEmpty(value))
{
var doc = XDocument.Parse(value);
int.TryParse( doc.Element("location").Element("id").Value, out result);
}
return result;
}
public Type TypeSupported { get { return typeof(int); } }
}
Additionally, we need to create an UmbracoProperty
attribute to explicilty indicate this type.
public class LocationIdUmbracoPropertyAttribute : UmbracoPropertyAttribute
{
public LocationIdUmbracoPropertyAttribute()
{
TypeHandler = new LocationIdTypeHandler();
}
}
And finally decorate our object with it.
public class Person {
public string Name { get; set; }
[LocationIdUmbracoProperty]
public int LocationId { get; set; }
}
Of course, you will need to register the type handler as described above.
For a great, simple example of how to extend Vault, check out the UmbracoVault.uComponents project.