[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; }
Таким образом, благодаря реализации трех точек расширения Вы можете добавить свой обработчик свойств объектов.