Проверка команд перед выполнением - PullRequest
2 голосов
/ 29 декабря 2011

В системе, которую я сейчас собираю, я использую шаблон команд для выполнения всех возможных операций. Я выбрал подход CommandMessage и CommandHandler, отделяющий логику от данных. Пока это работает нормально, но я столкнулся с проблемой - проверка.

Как мне на самом деле проверить, может ли команда выполняться или нет?

Прямо сейчас у меня есть CanExecute(ICommandExecutionContext context) для каждой команды, что делает ее ответственной за определение того, может ли она выполняться. Затем ICommandExecutionContext проверяется типом в каждой команде, чтобы узнать, имеет ли он правильный тип контекста, и впоследствии, если информация делает команду исполняемой в этом контексте.

Все обернуто в ICommandService, который может проверять и выполнять команды, основываясь на их именах, контексте и сообщении. Кроме того, он также публикует события о выполнении команд и выполняет проверки прав доступа.

Проблема связана с пользовательским интерфейсом (приложение ASP.NET MVC 3). Я хотел бы показывать только действительные команды в каждом представлении, но мне не удалось найти решение, которое мне действительно нравится. В настоящее время мой контроллер спрашивает службу команд, может ли команда выполняться, учитывая конкретный контекст, подобный следующему:

var executionContext = new SystemCommandExecutionContext("SignInCommand", CurrentPrincial);
var canExecute = _commandService.CanExecute(executionContext);
/* Handle the result to enable or disable the action link */

Для других типов команд, которые работают с конкретными объектами домена, я использую тот же метод обслуживания команд, но в другом контексте, где я передаю идентификатор объекта домена, например:

[HttpPost, Authorize, ValidateAntiForgeryToken /* etc. */]
public ActionResult Delete(Guid id)
{
    /* Note the additional object id in the context */
    var executionContext = new EntityCommandExecutionContext("DeletePersonCommand", CurrentPrincipal, id);
    var canExecute = _commandService.CanExecute(executionContext);

    if(canExecute)
    {
        var message = new DeletePersonCommandMessage(id);
        var isValid = _commandService.IsValid(executionContext, message);
        if(isValid)
        {
            var result = _commandService.Execute(executionContext, message);
            /* More logic here... Not very DRY :( */
        }            
    }
}

Полагаю, вышесказанное пока нормально, но не очень СУХОЕ. Но то, что я хотел бы сделать, это отключить ссылки действий, основанные на результате CanExecute.

Как я могу это сделать?

Я решил «жестко закодировать» все ссылки на команды в каждом представлении, поэтому мне не нужно передавать набор имен команд и т. Д. - этот путь слишком сложен (если у кого-то нет умной идеи;)

Мой текущий стек состоит из NHibernate, Castle Windsor, ASP.NET MVC 3, AutoMapper.

1 Ответ

1 голос
/ 30 декабря 2011

Как небольшое примечание, ваши объекты команд - это те, которые сериализуются и отправляются (возможно, удаленной) службе для обработки обработчиком команд, тогда это не экземпляр шаблона команды в смысле GoF, потому что объект команды Сам по себе не предоставляет метод исполнения. Цель объекта командного сообщения в этом случае состоит в том, чтобы представлять действие вместе с необходимыми параметрами. Некоторые источники называют это сериализуемым вызовом метода. Объекты сообщения не должны содержать поведение, только данные, и командное сообщение не является исключением. Это означает, что процесс определения того, может ли данная команда быть выполнена в конкретном контексте, должен обрабатываться отдельной службой. Конкретная реализация этой службы проверки зависит от критериев, используемых при определении возможности выполнения данной команды. Может быть в состоянии проверить команду на основе некоторого контекста и типа команды:

interface ICommandValidationService
{
  bool CanExecute(object context, Type commandType);
}

Объект контекста должен быть независимым от фактической выполняемой команды. Вместо этого он должен содержать более глобальные контекстные значения, такие как пользователь, права пользователя и т. Д. Возможность выполнения команды будет определяться с использованием этого контекста.

Отключение или скрытие ссылок действий на основе состояния выполнения команды может быть реализовано с использованием модели представления, подобной этой:

class ActionLinkViewModel
{
        public string Name { get; set; }
        public string Url { get; set; }
        public bool Enabled { get; set; }
}

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

...