[ELMA3] Создание собственного обработчика свойства
В данной статье будет приведен пример работы обработчика свойства типа Дата/время. Данный обработчик можно применять для любой переменной типа Дата/время, его работа заключается в том, что при создании объекта в данное поле будет подставляться дата создания автоматически. Такое поведение будет реализовано при помощи реализации трех точек расширения:
- IPropertyHandler – обработчик свойства. Задает имя обработчика, его уникальный идентификатор и определяет при каких условиях данный обработчик будет отображаться (для каких типов свойств).
- IEntityActivationHandler – точка расширения для перехвата активации при создании сущностей через InterfaceActivator.Create, InstanceOf или EntityManager.Create. Задает поведение сущности при активации, например, задается уникальный идентификатор и дата создания сущности.
- EntityEventsListener – базовый класс точки расширения для прослушивания событий NHibernate для объектов. Задает поведение при различных событиях NHibernate, например, перед вставкой объекта в базу задать дату создания. Реализация данной точки расширения необходима, так как в сценариях (в Дизайнере ELMA) пользователи часто создают сущности через new Entity(), при этом активации сущности уже не произойдет, но после вызова метода Save(), код в EntityEventsListener выполнится. Подробнее про обработчиков событий можно узнать в статье "Обработчики событией NHibernate".
Пример отображения
Рис. 1. Указание обработчика для свойства типа дата/время
Обработчик для свойства Дата создания является наследником базового класса TypedPropertyHandler, который в свою очередь является реализацией точки расширения IPropertyHandler.
Сигнатура точки расширения IPropertyHandler.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | /// <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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | /// <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 ; } } |
Обработчик свойства Дата создания
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | /// <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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | [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:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public override bool OnPreInsert(PreInsertEvent @ event ) { var entity = @ event .Entity as IEntity; if (entity == null ) { return false ; } // Заполнить свойства сущности. PropertyHandlersActivation.ActivateOnCreate(entity); return false ; } |
Таким образом, благодаря реализации трех точек расширения Вы можете добавить свой обработчик свойств объектов.