Creating a custom property handler

This article provides an example of a handler for a Date/Time-type property. This handler can be applied to any variable of the Date/Time type, its function is to automatically specify the creation date in the field when creating the object. Such a behavior is implemented using three extension points:

  1. IPropertyHandler – property handler. Defines the handler name, UID and the conditions, under which this handler will be displayed (for which property types).
  2. IEntityActivationHandler – extension point for listening to activation when creating entities via InterfaceActivator.Create, InstanceOf or EntityManager.Create. Defines the entity behavior at activation, e.g. UID and entity creation date are set.
  3. EntityEventsListener – base class of the extension point for listening to NHibernate events for objects. Defines the behavior for different NHibernate events, e.g. specify the creation date before adding the object to the database. This extension point must be implemented since uses often create entities using new Entity() in scripts (in ELMA Designer) and the entity is not activated at that, but after calling the Save() method, the code in EntityEventsListener will be executed. To learn more about event handlers, read this article.

Example

 

Fig. 1. Selecting a handler for a date/time property

Handler for the Creation Date property is a child of the base class TypedPropertyHandler, which in turn is an implementation of the IPropertyHandler extension point.

IPropertyHandler extension point signature.

/// <summary>

/// Handler UID

/// </summary>

Guid Uid { get; }

 

/// <summary>

/// Handler name (for the current culture)

/// </summary>

string Name { get; }

 

/// <summary>

/// Whether the handler is available (can be added)

/// </summary>

/// <param name="classMetadata">Class metadata</param>

/// <param name="propertyMetadata">Metadata of the property, to check handler availability for</param>

/// <param name="typeUid">Date type Uid</param>

/// <param name="subTypeUid">data subtype Uid</param>

/// <param name="currentHandlers">current handlers list</param>

/// <returns>True, if available</returns>

bool IsAvailableFor(

    ClassMetadata classMetadata, PropertyMetadata propertyMetadata, Guid typeUid, Guid subTypeUid,

    IEnumerable<PropertyHandlerInfo> currentHandlers);

 

Example of the TypedPropertyHandler base class:

/// <summary>

/// Property handler base class

/// </summary>

[Component]

public abstract class TypedPropertyHandler : IPropertyHandler

{

 

    /// <summary>

    /// Handler UID

    /// </summary>

    public abstract Guid Uid

    {

        get;

    }

 

    /// <summary>

    /// Handler name (for the current culture)

    /// </summary>

    public abstract string Name

    {

        get;

    }

 

    /// <summary>

    /// UID of the type for which the handler is intended

    /// </summary>

    protected abstract Guid? TypeUid

    {

        get;

    }

 

    /// <summary>

    /// UID of the subtype, for which the handler is intended

    /// </summary>

    protected virtual Guid? SubTypeUid

    {

        get { return Guid.Empty; }

    }

 

    /// <summary>

    /// Can several handlers of this type be added (by default False)

    /// </summary>

    protected virtual bool Multiple

    {

        get { return false; }

    }

 

    /// <summary>

    /// Can other handlers be added (by default False)

    /// </summary>

    protected virtual bool AllowOtherHandlers

    {

        get { return false; }

    }

 

    /// <summary>

    /// Whether the handler is available (can be added)

    /// </summary>

    /// <param name="classMetadata">Class metadata</param>

    /// <param name="propertyMetadata">Metadata of the property, to check the handler availability for</param>

    /// <param name="typeUid">data type Uid</param>

    /// <param name="subTypeUid">data subtype Uid</param>

    /// <param name="currentHandlers">Current handlers list</param>

    /// <returns>True, if available</returns>

    public virtual bool IsAvailableFor(

        ClassMetadata classMetadata, PropertyMetadata propertyMetadata, Guid typeUid, Guid subTypeUid,

        IEnumerable<PropertyHandlerInfo> currentHandlers)

    {

        Contract.ArgumentNotNull(classMetadata, "classMetadata");

        Contract.ArgumentNotNull(propertyMetadata, "propertyMetadata");

        var handlers = currentHandlers != null ? currentHandlers.ToList() : new List<PropertyHandlerInfo>();

        if (!Multiple)

        {

            if (handlers.FirstOrDefault(h => h.HandlerUid == Uid) != null)

            {

                return false;

            }

        }

        if (!AllowOtherHandlers)

        {

            if (handlers.Count > 0)

            {

                return false;

            }

        }

        if (TypeUid == null)

        {

            return true;

        }

        if (TypeUid.Value != typeUid)

        {

            return false;

        }

        if (SubTypeUid == null)

        {

            return true;

        }

        if (SubTypeUid.Value != subTypeUid)

        {

            return false;

        }

        return true;

    }

}

 

Creation Date property handler

/// <summary>

/// Creation Date property handler

/// </summary>

[Component(Order = 100)]

public class CreationDatePropertyHandler : TypedPropertyHandler

{

    /// <summary>

    /// This handler UID

    /// </summary>

    public const string UID_S = "{D0C00D8A-E003-427D-9942-F52CFB77B0F0}";

 

    /// <summary>

    /// this handler UID

    /// </summary>

    public static readonly Guid UID = new Guid(UID_S);

 

    /// <summary>

    /// Handler UID

    /// </summary>

    public override Guid Uid

    {

        get { return UID; }

    }

 

    /// <summary>

    /// Handler name (for the current culture)

    /// </summary>

    public override string Name

    {

        get { return SR.T("Creation Date"); }

    }

 

    /// <summary>

    /// Uid of the type for which the handler is intended

    /// </summary>

    protected override Guid? TypeUid

    {

        get { return DateTimeDescriptor.UID; }

    }

 

}

 

All the system handlers are heirs of the TypedPropertyHandler base class, only the name is different, Uid and TypeUid.

To specify the required value, you need to implement the IEntityActivationHandler:

[Component]

internal class PropertyHandlersActivation : IEntityActivationHandler

{

 

    /// <summary>

    /// Fill in the entity properties

    /// </summary>

    /// <param name="entity">Entity</param>

    public static void ActivateOnCreate(IEntity entity)

    {

        var metadata = MetadataLoader.LoadMetadata(entity.GetType(), true) as EntityMetadata;

 

        if (metadata == null)

        {

            return;

        }

 

        // Creation Date

        var creationDateProperty = metadata.Properties.FirstOrDefault(p => p.Handlers.FirstOrDefault(h => h.HandlerUid == CreationDatePropertyHandler.UID) != null);

        if (creationDateProperty != null

            && (entity.GetPropertyValue(creationDateProperty.Uid) == null

            || (DateTime)entity.GetPropertyValue(creationDateProperty.Uid) == DateTime.MinValue))

        {

            entity.SetPropertyValue(creationDateProperty.Uid, DateTime.Now);

        }

    }

 

    public void OnActivating(Model.Entities.IEntity entity)

    {

        ActivateOnCreate(entity);

    }

 

    public void OnActivated(Model.Entities.IEntity entity)

    {

             

    }

 

}

 

The ActivateOnCreate method will be called in the EntityEventsListener in the OnPreInsert method for all the IEntity entities:

public override bool OnPreInsert(PreInsertEvent @event)

{

    var entity = @event.Entity as IEntity;

 

    if (entity == null)

    {

        return false;

    }

    // Fill in the entity properties.

    PropertyHandlersActivation.ActivateOnCreate(entity);

 

    return false;

}

Thus, by implementing three extension points, you can add your custom object properties handler.