[ELMA3] Работа с действиями в сущностях и менеджерах
Действия в архитектуре системы
Для выполнения каких-либо операций с сущностями необходимо создавать менеджеры, наследуемые от EntityManager. В этих менеджерах определяются всевозможные операции как публичные методы:
/// <summary> /// Блокировать учетную запись пользователя /// </summary> /// <param name="user">Пользователь</param> public void Block(Models.IUser user)
В этих методах производится проверка возможности выполнения, и вызывающий код должен обязательно корректно обработать возможные исключения. Также нередко доступность таких действий привязана к набору привилегий. Часто вызывающий код самостоятельно должен проверять (т.е. дублировать проверку) доступность действия, чтобы корректно построить интерфейс.
Механизм работы через действия, привязанные к сущности, позволяет решить проблему дублирования проверок и предоставляет возможности использовать действия вне зависимости от контекста: веб-приложение, скрипты, внешняя интеграция.
Реализация действий в привязке к сущностям и менеджерам
Описание начинается с дизайнера сущности. Во вкладке Действия задается список возможных действий, связанных с сущностью (рис. 1).
Рис. 1. Дизайнер ELMA. Карточка объекта. Вкладка "Действия"
На этом этапе важно понимать, что тут закладывается логика сущности, ее дальнейшее поведение и возможности работы с сущностью.
Далее, после сохранения модели, по списку действий генерируется набор ключей для действий.
//тут код, сгенерированный по списку действий
Но сами по себе эти ключи не несут логики. Как раз ее и реализуют в методах менеджера для данной сущности. Чтобы отметить метод как реализацию действия, необходимо пометить его атрибутом EleWise.ELMA.Actions.ActionMethodAttribute, например, для действия Блокировать необходимо написать такой код:
/// <summary> /// Блокировать учетную запись пользователя /// </summary> /// <param name="user">Пользователь</param> [EleWise.ELMA.Actions.ActionMethod(UserActions.Block)] public virtual void Block(Models.IUser user) { //Тут только код реализации действия, без проверок }
Обратите внимание, метод помечен как virtual и имеет видимость public – это обязательное условие для корректной работы внутренней логики.
Но этого недостаточно, так как мы не проверили возможность выполнения этого действия и не указали, какие привилегии нужны для выполнения действия. Для начала привяжем наш метод к привилегиям, пометив его атрибутом EleWise.ELMA.Actions.ActionPermissionAttribute:
/// <summary> /// Блокировать учетную запись пользователя /// </summary> /// <param name="user">Пользователь</param> [EleWise.ELMA.Actions.ActionMethod(UserActions.Block)] [ActionPermission(PermissionProvider.UserManagmentPermissionId)] public virtual void Block(Models.IUser user)
Набор привилегий задается при создании модуля. В данном случае мы привязали возможность блокирования пользователя к привилегии Управление пользователями.
Далее нам необходимо проверить, что пользователь, учетную запись которого мы хотим блокировать, активен и не является системной учетной записью администратора. Для этого нам нужно создать в менеджере еще один метод, с точно таким же набором входящих аргументов и возвращающий значение bool, пометить его атрибутом EleWise.ELMA.Actions.ActionCheckAttribute и указать в качестве действия тот же ключ, что и для основного метода:
/// <summary> /// Проверка возможности выполнения действия /// </summary> /// <param name="user">Пользователь</param> /// <returns><c>true</c>, если можно выполнить действие</returns> [EleWise.ELMA.Actions.ActionCheck(UserActions.Block)] protected virtual bool CanBlock(Models.IUser user) { return user != null && !IsNew(user) && user.Status == UserStatus.Active && user.UserName != "admin"; }
Так же, как и основной метод, этот должен быть помечен как virtual, но в отличие от основного может иметь видимость protected.
Теперь при вызове в любом месте метода UserManager.Instance.Block(user) сначала будет произведена проверка привилегий, затем проверка при помощи метода CanBlock, и только потом будет выполнено само действие.
Работа с действиями через сервисы системы
Для работы с действиями существует специальный сервис EleWise.ELMA.Actions.ActionDispatcherService в нем всего 2 нужных для нас метода:
/// <summary> /// Проверка возможности выполнения действия. /// Нет необходимости вызывать перед выполнением. /// </summary> /// <param name="actionObject">Объект действия</param> /// <param name="actionType">Тип действия</param> /// <param name="methodArgs">Аргументы, передаваемые в действие</param> /// <returns>Можно ли выполнять действие в данном контексте</returns> public bool CheckAction(IAuditObject actionObject, IAuditAction actionType, params object[] methodArgs) /// <summary> /// Выполнить действие (будет произведена проверка) /// </summary> /// <param name="actionType">Тип действия</param> /// <param name="actionObject">Объект действия</param> /// <param name="methodArgs">Аргументы, передаваемые в действие</param> /// <returns>Результат выполнения действия</returns> /// <exception cref="System.InvalidOperationException">Если CheckAction вернет false</exception> public object InvokeAction(IAuditObject actionObject, IAuditAction actionType, params object[] methodArgs)
Как вы можете видеть, в качестве параметров используются обекты типа IAuditObject и IAuditAction. Эти объекты связывают системы действий и событий таким образом, что для любого объекта и события из системы событий можно реализовать выполнение данных действий.