[ELMA3] Создание собственного обработчика свойства
В данной статье будет приведен пример работы обработчика свойства типа Дата/время. Данный обработчик можно применять для любой переменной типа Дата/время, его работа заключается в том, что при создании объекта в данное поле будет подставляться дата создания автоматически. Такое поведение будет реализовано при помощи реализации трех точек расширения:
- IPropertyHandler – обработчик свойства. Задает имя обработчика, его уникальный идентификатор и определяет при каких условиях данный обработчик будет отображаться (для каких типов свойств).
- IEntityActivationHandler – точка расширения для перехвата активации при создании сущностей через InterfaceActivator.Create, InstanceOf или EntityManager.Create. Задает поведение сущности при активации, например, задается уникальный идентификатор и дата создания сущности.
- EntityEventsListener – базовый класс точки расширения для прослушивания событий NHibernate для объектов. Задает поведение при различных событиях NHibernate, например, перед вставкой объекта в базу задать дату создания. Реализация данной точки расширения необходима, так как в сценариях (в Дизайнере ELMA) пользователи часто создают сущности через new Entity(), при этом активации сущности уже не произойдет, но после вызова метода Save(), код в EntityEventsListener выполнится. Подробнее про обработчиков событий можно узнать в статье "Обработчики событией NHibernate".
Пример отображения

Рис. 1. Указание обработчика для свойства типа дата/время
Обработчик для свойства Дата создания является наследником базового класса TypedPropertyHandler, который в свою очередь является реализацией точки расширения IPropertyHandler.
Сигнатура точки расширения IPropertyHandler.
/// <summary>
/// Уникальный идентификатор обработчика
/// </summary>
Guid Uid { get; }
/// <summary>
/// Имя обработчика (для текущей культуры)
/// </summary>
string Name { get; }
/// <summary>
/// Доступен ли обработчик (можно ли добавить)
/// </summary>
/// <param name="classMetadata">Метаданные класса</param>
/// <param name="propertyMetadata">Метаданные свойства, для которого нужно проверить доступность обработчика</param>
/// <param name="typeUid">Uid типа данных</param>
/// <param name="subTypeUid">Uid подтипа данных</param>
/// <param name="currentHandlers">Список текущих обработчиков</param>
/// <returns>True, если доступен</returns>
bool IsAvailableFor(
ClassMetadata classMetadata, PropertyMetadata propertyMetadata, Guid typeUid, Guid subTypeUid,
IEnumerable<PropertyHandlerInfo> currentHandlers);
Пример базового класса TypedPropertyHandler:
/// <summary>
/// Базовый класс обработчика свойства
/// </summary>
[Component]
public abstract class TypedPropertyHandler : IPropertyHandler
{
/// <summary>
/// Уникальный идентификатор обработчика
/// </summary>
public abstract Guid Uid
{
get;
}
/// <summary>
/// Имя обработчика (для текущей культуры)
/// </summary>
public abstract string Name
{
get;
}
/// <summary>
/// Uid типа, для которого предназначен обработчик
/// </summary>
protected abstract Guid? TypeUid
{
get;
}
/// <summary>
/// Uid подтипа, для которого предназначен обработчик
/// </summary>
protected virtual Guid? SubTypeUid
{
get { return Guid.Empty; }
}
/// <summary>
/// Можно ли навешивать несколько обработчиков данного типа (по умолчанию False)
/// </summary>
protected virtual bool Multiple
{
get { return false; }
}
/// <summary>
/// Могут ли быть навешены другие обработчики (по умолчанию False)
/// </summary>
protected virtual bool AllowOtherHandlers
{
get { return false; }
}
/// <summary>
/// Доступен ли обработчик (можно ли добавить)
/// </summary>
/// <param name="classMetadata">Метаданные класса</param>
/// <param name="propertyMetadata">Метаданные свойства, для которого нужно проверить доступность обработчика</param>
/// <param name="typeUid">Uid типа данных</param>
/// <param name="subTypeUid">Uid подтипа данных</param>
/// <param name="currentHandlers">Список текущих обработчиков</param>
/// <returns>True, если доступен</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;
}
}
Обработчик свойства Дата создания
/// <summary>
/// Обработчик свойства "Дата создания"
/// </summary>
[Component(Order = 100)]
public class CreationDatePropertyHandler : TypedPropertyHandler
{
/// <summary>
/// UID данного обработчика
/// </summary>
public const string UID_S = "{D0C00D8A-E003-427D-9942-F52CFB77B0F0}";
/// <summary>
/// UID данного обработчика
/// </summary>
public static readonly Guid UID = new Guid(UID_S);
/// <summary>
/// Уникальный идентификатор обработчика
/// </summary>
public override Guid Uid
{
get { return UID; }
}
/// <summary>
/// Имя обработчика (для текущей культуры)
/// </summary>
public override string Name
{
get { return SR.T("Дата создания"); }
}
/// <summary>
/// Uid типа, для которого предназначен обработчик
/// </summary>
protected override Guid? TypeUid
{
get { return DateTimeDescriptor.UID; }
}
}
Все системные обработчики являются наследниками базового класса TypedPropertyHandler, отличия лишь в названии, Uid и TypeUid
Чтобы заполнить свойство нужным значением, необходимо реализовать перехватчик активации сущности IEntityActivationHandler:
[Component]
internal class PropertyHandlersActivation : IEntityActivationHandler
{
/// <summary>
/// Заполнить свойства сущности
/// </summary>
/// <param name="entity">Сущность</param>
public static void ActivateOnCreate(IEntity entity)
{
var metadata = MetadataLoader.LoadMetadata(entity.GetType(), true) as EntityMetadata;
if (metadata == null)
{
return;
}
// Дата создания
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)
{
}
}
Метод ActivateOnCreate будет вызван в листенере EntityEventsListener в методе OnPreInsert для всех сущностей IEntity:
public override bool OnPreInsert(PreInsertEvent @event)
{
var entity = @event.Entity as IEntity;
if (entity == null)
{
return false;
}
// Заполнить свойства сущности.
PropertyHandlersActivation.ActivateOnCreate(entity);
return false;
}
Таким образом, благодаря реализации трех точек расширения Вы можете добавить свой обработчик свойств объектов.