Работа с действиями в сущностях и менеджерах

Действия в архитектуре системы

Для выполнения каких-либо операций с сущностями необходимо создавать менеджеры, наследуемые от EntityManager. В этих менеджерах определяются всевозможные операции как публичные методы:
/// <summary>
/// Блокировать учетную запись пользователя
/// </summary>
/// <param name="user">Пользователь</param>
public void Block(Models.IUser user)
В этих методах производится проверка возможности выполнения, и вызывающий код должен обязательно корректно обработать возможные исключения. Также нередко доступность таких действий привязана к набору привилегий. Часто вызывающий код самостоятельно должен проверять (т.е. дублировать проверку) доступность действия, чтобы корректно построить интерфейс.

Механизм работы через действия, привязанные к сущности, позволяет решить проблему дублирования проверок и предоставляет возможности использовать действия вне зависимости от контекста: веб-приложение, скрипты, внешняя интеграция.

Реализация действий в привязке к сущностям и менеджерам

Описание начинается с дизайнера сущности. Во вкладке Действия задается список возможных действий связанных с сущностью.

На этом этапе важно понимать, что тут закладывается логика сущности, ее дальнейшее поведение и возможности работы с сущностью.

Далее, после сохранения модели, по списку действий генерируется набор ключей для действий.
//тут код, сгенерированный по списку действий
Но сами по себе эти ключи не несут логики. Как раз ее и реализуют в методах менеджера для данной сущности. Чтобы отметить метод как реализацию действия, необходимо пометить его атрибутом 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. Эти объекты связывают системы действий и событий таким образом, что для любого объекта и события из системы событий можно реализовать выполнение данных действий.

Ссылки на элементы API

ActionDispatcherService.