Создание веб-сайта для отслеживания ошибок с помощью Windows Workflow Foundation — Часть 3

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

Как рабочий процесс интегрируется с веб-сайтом

На данном этапе может быть полезно обсудить, как веб-сайт будет интегрирован с WF. Поскольку я использую MVC, мои контроллеры обычно зависят от уровня обслуживания. Этот уровень сервиса, в свою очередь, зависит от уровня хранилища для загрузки и сохранения данных. Сервисный уровень полезен для фильтрации данных (например, загрузки ошибок для конкретного пользователя) или проверки данных перед их сохранением. Я хотел бы сохранить эту структуру, поэтому я собираюсь изменить BugService в зависимости от службы рабочего процесса (в дополнение к BugRepository ) для выполнения любых действий, связанных с рабочим процессом.

Давайте посмотрим, как мы на самом деле размещаем и запускаем среду выполнения WF.

Размещение среды выполнения Workflow Foundation

Настройка и запуск среды выполнения WF — это относительно простое упражнение. Опять же, мы можем сделать эту конфигурацию либо из кода, либо в файле конфигурации. Так как нам нужно будет указать строку подключения для сохранения рабочего процесса (которое обязательно изменится), я использовал файл конфигурации.

private readonly IUnityContainer container;
private WorkflowRuntime WorkflowRuntime { get; set; }

public WorkflowService(IUnityContainer container)
{
    this.container = container;

    WorkflowRuntime = new WorkflowRuntime("workflowConfiguration");

    WorkflowRuntime.WorkflowCompleted += WorkflowCompleted;
    WorkflowRuntime.WorkflowTerminated += WorkflowTerminated;
    WorkflowRuntime.WorkflowIdled += WorkflowIdled;

    AddServices();

    WorkflowRuntime.StartRuntime();
}

Вот сложная часть — нам нужно поддерживать один экземпляр этой среды выполнения. Нам также нужно зарегистрировать BugTrackingService и BugTransactionService с этим единственным экземпляром и иметь возможность получить указатель на зарегистрированный BugTrackingService, чтобы иметь возможность вызывать события при необходимости. Давайте сначала посмотрим, как мы на самом деле регистрируем эти сервисы во время выполнения. (Обратите внимание, что это НЕ идеальный способ сделать это)

private void AddServices()
{
    var dataExchangeService = new ExternalDataExchangeService();
    dataExchangeService.AddService(new BugTrackingService());

    WorkflowRuntime.AddService(dataExchangeService);
    WorkflowRuntime.AddService(new BugTransactionService());
}

Хотя этот подход будет работать, он ломается по нескольким направлениям. Во-первых, нам нужна ссылка на BugTrackingService, и я хотел бы оставить сервис Workflow универсальным — ему не нужно знать о BugTrackingService или BugTransactionService . Это где Unity вступает в игру.

Использование Unity для настройки среды выполнения Workflow Foundation

Я собираюсь использовать Unity для регистрации всех служб обмена данными и транзакций, которые мне требуются, а затем добавить любую зарегистрированную службу во время выполнения. Делаем регистрационную часть в Bootstrapper .

var bugTrackingService = new BugTrackingService();
container.RegisterInstance<IBugTrackingService>(bugTrackingService);
container.RegisterInstance<IExternalDataExchange>(typeof(BugTrackingService).FullName, bugTrackingService);
container.RegisterType<IPendingWork, BugTransactionService>(typeof(BugTransactionService).FullName, new ContainerControlledLifetimeManager());

container.RegisterType<IWorkflowService, WorkflowService>(new ContainerControlledLifetimeManager());

Здесь нужно отметить 2 вещи. Во-первых, я регистрирую BugTrackingService дважды — один раз с именем и один раз без имени. Это связано с тем, что метод ResolveAll будет возвращать только именованные регистрации — этот метод мы будем использовать при получении зарегистрированных служб при инициализации во время выполнения. Во-вторых, ContainerControllerLifetimeManager — это правильный способ указать, что зарегистрированный класс является одиночным. (К сожалению, я не могу использовать этот синтаксис для регистрации BugTrackingService — именованные и безымянные регистрации будут возвращать разные экземпляры синглтона.)

Теперь, когда сервисы зарегистрированы, я могу добавить все зарегистрированные сервисы во время выполнения.

private void AddServices()
{
    var dataExchangeService = new ExternalDataExchangeService();
    WorkflowRuntime.AddService(dataExchangeService);

    foreach (var externalDataExchange in container.ResolveAll<IExternalDataExchange>())
    {
        dataExchangeService.AddService(externalDataExchange);
    }

    foreach (var pendingWorkService in container.ResolveAll<IPendingWork>())
    {
        WorkflowRuntime.AddService(pendingWorkService);
    }
}

Обратите внимание, что теперь WorkflowService зависит от самого контейнера Unity, поэтому я зарегистрировал этот контейнер в первой части этой серии.

Служба ошибок

Как я уже упоминал, я собираюсь реализовать BugService, чтобы зависеть от BugRepository, а также от WorkflowService (который теперь является универсальным сервисом для управления всеми задачами рабочего процесса).

public class BugService : IBugService
{
    private readonly IBugRepository bugRepository;
    private readonly IBugTrackingService bugTrackingService;
    private readonly IWorkflowService workflowService;

    public BugService(IBugRepository bugRepository, IBugTrackingService bugTrackingService, IWorkflowService workflowService)
    {
        this.bugRepository = bugRepository;
        this.bugTrackingService = bugTrackingService;
        this.workflowService = workflowService;
    }

    public IQueryable<Bug> GetBugs()
    {
        return bugRepository.GetBugs();
    }

    public void UpdateBug(Bug bug)
    {
        bugRepository.UpdateBug(bug);
    }

    public void AddBug(Bug bug)
    {
        var workflowId = workflowService.StartWorkflow(typeof(Workflow), WorkflowStatus.Running);

        bug.WorkflowInstanceId = workflowId;
        bugTrackingService.BugOpened(bug);

        workflowService.RunWorkflow(bug.WorkflowInstanceId, WorkflowStatus.Running);
    }

    public void CloseBug(Bug bug)
    {
        bugTrackingService.BugClosed(bug);
        workflowService.RunWorkflow(bug.WorkflowInstanceId, WorkflowStatus.Completed);
    }

    public BugState GetPossibleTransitions(Bug bug)
    {
        var possibleTransitions = BugState.None;
        foreach (var state in workflowService.GetStateMachineTransitions(bug.WorkflowInstanceId))
        {
           possibleTransitions |= (BugState)Enum.Parse(typeof(BugState), state.Replace("State", ""));
        }

        return possibleTransitions;
    }
}

Надеюсь, вы сможете увидеть, как сайт будет взаимодействовать с рабочим процессом (я пропустил некоторые переходы).

Есть 2 метода, которые выделяются здесь. Во-первых, метод UpdateBug позволяет нам обновить ошибку без выполнения перехода рабочего процесса — это полезно, когда мы изменяем детали ошибки (например, назначенного пользователя) без изменения состояния. Второй — GetPossibleTransitions — это позволяет избежать жесткого кодирования возможных переходов на веб-сайте.

Резюме

Я просмотрел некоторые детали запуска рабочего процесса и получения результатов. Большинство из этих концепций мастерски освещены Скоттом Алленом, и я не чувствовал необходимости упоминать их. Я просто попытался уменьшить сцепление, улучшить тестируемость и улучшить дизайн, чтобы приспособить несколько рабочих процессов.

Если у вас есть какие-либо вопросы или комментарии, оставьте комментарий ниже.

Author: admin

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *