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

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

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

На этом этапе, возможно, стоит сделать шаг назад и попытаться понять, как все это будет сочетаться. Workflow Foundation (WF) позволяет нам сохранять информацию с помощью экземпляра рабочего процесса — не должны ли мы просто добавить объект ошибки в экземпляр рабочего процесса и позволить WF управлять всем постоянством для нас? Нужна ли нам таблица «Ошибки» в базе данных?

Хотя мы могли бы покончить с наличием таблицы в базе данных, я бы не рекомендовал ее. Мы по-прежнему будем получать доступ к ошибкам из внешнего интерфейса (веб-сайта), и я хочу иметь возможность выбирать все ошибки без использования среды выполнения рабочего процесса. Это означает, что рабочий процесс сохранит наши объекты в базе данных, а объект ошибки будет иметь состояние, которое рабочий процесс должен будет обновить. Объект ошибки также будет иметь ссылку на экземпляр рабочего процесса.

Проектирование рабочего процесса

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

Пока ничего особенного, я просто определил различные состояния для времени жизни ошибки, а также начальное и конечное состояния. Теперь я собираюсь добавить различные переходы между этими состояниями. Для этого мне сначала нужно определить интерфейс ExternalDataExchange . Это просто интерфейс, который содержит события, которые будут вызываться в коде, чтобы указать механизму рабочего процесса, что произошло внешнее событие. Позже мы предоставим экземпляр этого интерфейса во время выполнения рабочего процесса, а WF прикрепит обработчики событий ко всем этим событиям.

[ExternalDataExchange]
public interface IBugTrackingService
{
    event EventHandler<BugTrackingEventArgs> Opened;
    event EventHandler<BugTrackingEventArgs> Resolved;
    event EventHandler<BugTrackingEventArgs> Closed;
    event EventHandler<BugTrackingEventArgs> Deferred;
    event EventHandler<BugTrackingEventArgs> Assigned;
}
[Serializable]
public class BugTrackingEventArgs : ExternalDataEventArgs
{
    public Bug Bug { get; set; }

    public BugTrackingEventArgs(Guid instanceId, Bug bug) : base(instanceId)
    {
        Bug = bug;
    }
}

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

Здесь я определил событие и переход состояния для начального состояния (другие состояния очень похожи). Обратите внимание, что я не добавил действия по обновлению и сохранению ошибки в базе данных. Мне нужно будет сделать это с пользовательским действием.

Создайте пользовательскую активность для постоянства

Сначала я попытался вручную сохранить объект Bug и запустить рабочий процесс для выполнения перехода внутри транзакции. Хотя это будет работать, этот подход заставляет нас вручную выполнять некоторые шаги рабочего процесса вне фактического рабочего процесса (например, обновлять состояние ошибки), и мы не получим максимальную выгоду от использования рабочих процессов.

Что я действительно хочу, так это чтобы рабочий процесс обновлял мою ошибку и обрабатывал все постоянство. Имейте в виду, что это включает в себя сохранение объекта ошибки, а также состояние рабочего процесса. WF обрабатывает постоянство экземпляра рабочего процесса, и нам нужно подключить BugRepository к WF для сохранения объекта ошибки.

Настраиваемое действие будет выполнять 2 задачи — обновление состояния ошибки и сохранение объекта ошибки. Это означает, что мы должны передать и состояние, и объект ошибки в пользовательское действие. Мы делаем это с помощью свойств зависимости. (Если вы находите эту часть запутанной, взгляните на статью Скотта Аллена )

public static DependencyProperty BugProperty = DependencyProperty.Register("Bug", typeof(Bug), typeof(UpdateBugActivity));
public Bug Bug
{
    get
    {
        return (Bug) GetValue(BugProperty);
    }
    set
    {
        SetValue(BugProperty, value);
    }
}

public static DependencyProperty StateProperty = DependencyProperty.Register("State", typeof(string), typeof(UpdateBugActivity));
public string State
{
    get
    {
        return (string)GetValue(StateProperty);
    }
    set
    {
        SetValue(StateProperty, value);
    }
}

Эти 2 свойства будут установлены в конструкторе рабочих процессов. Мы свяжемсвойство Bug с объектом, передаваемым в BugTrackingEventArgs, а свойство State — с целевым состоянием в действии SetState .

Сначала нам нужно сохранить передаваемый событием BugTrackingEventArgs как свойство внутри нашего экземпляра рабочего процесса. Когда вы знаете, как это сделать, на самом деле это довольно просто. Сначала перейдите на вкладку свойств в действии HandleExternalEvent.

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

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

public partial class Workflow : StateMachineWorkflowActivity
{
    public BugTrackingEventArgs BugTrackingEventArgs = default(BugTrackingEventArgs);
}

Теперь нам просто нужно привязать это свойство к аргументу Bug в нашей пользовательской операции. Привязка аргумента State к нашей пользовательской деятельности немного отличается. Мы могли бы просто указать требуемое значение состояния здесь, но лучшей идеей является привязка к целевому состоянию в операции SetState . Таким образом, мы не дублируем целевое состояние — мы можем изменить переход, чтобы перейти в другое целевое состояние, и нам нужно только обновить действие SetState .

Сервис транзакций

Служба BugTransactionService необходима, чтобы позволить нам сохранить ошибку в той же транзакции, что и экземпляр рабочего процесса. Этот сервис транзакций просто делегирует постоянство нашему классу BugRepository. Нам нужно зарегистрировать экземпляр этого класса во время выполнения рабочего процесса.

public void Commit(Transaction transaction, ICollection items)
{
    foreach (Bug bug in items)
    {
        if (bug.Id == 0)
        {
            bugRepository.AddBug(bug);
        }
        else
        {
            bugRepository.UpdateBug(bug);
        }
    }
}

Единственным недостатком здесь является то, что нам нужно включить MSDTC . Это потому, что мы на самом деле можем хранить экземпляры рабочего процесса в другой базе данных, чем база данных, содержащая таблицу ошибок . Чтобы включить MSDTC, вам просто нужно запустить службу DTC.

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

Интернет по оптике в Миассе AirNet

Author: admin

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

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