понедельник, 5 января 2009 г.

Getting Started

Давайте рассмотрим использование Listma на примере.

Само название движка «Linked State Machine» подразумевает , что мы имеем бизнес-объект с состоянием, к которому нужно подключить workflow функционал.
Рассмотрим бизнес-объект «Order» (Заказ):

public class Order
{
public Order() { }
public string Number { get; set; }
public string Customer { get; set; }
public string Address { get; set; }
public string Product { get; set; }
public decimal Count { get; set; }
public decimal Price { get; set; }
public decimal Total { get { return Count * Price; } }
public OrderState State { get; set; }
public string ApproveState { get; set; }
List _history = new List();
public List History { get { return _history; } }
}


Состояние объекта Order описывается перечислением OrderState.

public enum OrderState
{
Draft,
Processing,
Canceled,
Archive
}

Обрисуем задачу, которая перед нами стоит. На первом этапе она минимальна. Мы хотим описать перечень возможных переходов между состояниями OrderStste. Этот перечень будет выглядеть так:

Draft -> Presessing
Draft -> Canceled
Processing -> Archive

Остальные переходы запрещены.

Опишем это все в виде диаграмы Listma xml statechart:

<?xml version="1.0" encoding="utf-8" ?>
<Statechart Id="OrderWorkflow1" xmlns="urn:Listma:Starechart">
<State Id="Draft" Title="Drart" Initial="true" >
<Transition Id="Send" Title="Send to processing" TargetState="Processing" />
<Transition Id="Cancel" Title="Cancel order" TargetState="Canceled" />
</State>
<State Id="Processing" Title="Processing" Initial="false" >
<Transition Id="Process" Title="Process order" TargetState="Archive" />
</State>
<State Id="Archive" Title="Archive" Initial="false" />
<State Id="Canceled" Title="Canceled" Initial="false" />
</Statechart>

На самом деле схема описания диаграмы Listma statechart намного более сложная, но нас сейчас не интересуют все остальные возможности и мы их просто опускаем.
Здесь также надо заметить, что имя xml файла, содержащего statechart должно совпадать со statechart Id.

Далее, нам надо сконфигурировать workflow для нашего бизнес-объекта Order. В чем смысл этой операции? Дело в том, что класс Order ничего не знает о Listma равно как и наоборот. Смысл конфигурирование workflow состоит в том, чтобы сообщить движку Listma необходимые сведения о бизнес объекте. Во-первых нам необходимо устаносить связь между бизнес объектом и назначенной ему диаграмой состояния (statechart). Во-вторых мы должны указать имя атирбута, который используется для хранения состояния объекта.
Конфигурации workflow для бизнес объектов хранятся в конфигурационном файле, простейший вариант которого приведен ниже:

<ListmaConfiguration xmlns="urn:Listma:configuration"
StatechartDir="">
<EntityWorkflow EntityType="Listma.Test.Order"
StatechartId="OrderWorkflow1"
InitialState="*"
StateMap ="State" />
<EntityWorkflow EntityType="OrderApproval"
StatechartId="OrderApprovalWorkflow1"
StateMap ="ApproveState" />
</ListmaConfiguration>

Для связи workflow с бизнес объектом служит атрибут «EntityType». Это может быть как полное имя класса (по умолчанию) так и призвольное имя. В данном примере бизнес-объекту назначены два workflow. Первый описывается диаграммой (StechartId) «OrderWorkflow1» и использует для хранения состояния атррибут объекта «State». Второй описывается диагораммой «OrderApprovalWorkflow1» и использует для хранения состояния атррибут объекта «ApproveState».

После того, как движок сконфигурирован, мы можем его использовать:
[Test]
public void SimpleWorkflowScenario()
{
Order order = Order.GetOrder();
ListmaManager lm = new ListmaManager();

IWorkflowAdapter w = lm.GetWorkflow(order);
// плучаем список допустимых переходов
TransitionInfo[] transitions = lm.GetAvailableTransitions(w);

Assert.AreEqual(2, transitions.Length);


// выполняем перевод объекта в новое состояние
lm.DoStep(w, "Send");

Assert.AreEqual("Processing", w.CurrentState);
}

Примечание: для того, чтобы приведенный код успешно выполнился, необходимо, чтобы приведенные выше конфигурационный файл с именем listma.config и файл statechart с именем OrderWorkflow1.xml распологались в каталоге запуска приложения. О том как указать расположение конфигурационных файлов, а также о том как конфигурировать Listma непосредственно в коде, будет рассказано позже.

Итак, что делает приведенный код?
Прежде всего для работы мы всегда используем экземпляр класса ListmaManager. Это сервис-фасад движка.
Для работы с workflow назначенным нашему бизнес-объекту мы должны получить экземпляр generic интерфейса IWorkflowAdapter, для чего в классе ListmaManager существует ряд перегруженных методов GetWorkflow().
Метод ListmaManager.GetWorkflow(T entity) используется, если при конфигурировании мы указали в качестве EntityType полное имя класса.
Если для связи объекта с workflow мы указали д\произвольое имя, то для получения IWorkflowAdapter нам следует использовать другой перегруженный метод: ListmaManager.GetWorkflow(T entity, string entityType).

Помимо методов GetWorkflow() в классе ListmaManager существует ряд перегруженных методов StartWorkflow(). Эти методы работают аналогично GetWorkflow, но отличаются тем что инициализируют начальное состояние объекта. Для этого используется значение атрибута «InitialState» из конфигурации workflow, либо, при его отсутствии (или если в нем указано «*») первое из состояний диаграмы, помеченное атрибутом Initial="true".

Метод ListmaMenedger.GetAvailableTransitions() позволяет получить список доступных переходов в соответствии с текущим состоянием объекта и ролью пользователя.

Метод ListmaManager.DoStep() выполняет перевод объекта в новое состояние. В данном примере это единственное, что он делает. На самом деле это основной метод движка, и вокруг него строится большая часть всей логики workflow. Но об этом позже

5 комментариев:

  1. Собственно, вроде бы опечатка в первое xml:
    <Statechart Id="OrderWorkflow1" xmlns="urn:Listma:Starechart"
    Пространство имён, по идее, должно быть Statechart.

    Ещё пара опечаток в других постах, плюс, дефолтный блоггеровский шаблон очень узкий, длинные строки с кодом не переносятся и уходят за границу <div> :(

    ОтветитьУдалить
  2. Спасибо. Опечатку исправил в исходниках, а инсталятор обновлю позже.

    ОтветитьУдалить
  3. Если интересно, могу просмотреть остальные посты не тему опечаток и выложить потом список.

    ОтветитьУдалить
  4. Да, можете отправить на sroovik[собака]gmail.com

    ОтветитьУдалить
  5. Сергей, отличная работа ! Уже давно заметил аноснс проекта в вашем дневнике, но только сейчас "руки дошли" посмотреть. Тема workflow очень интересная, причем не так давно я сам решил обобщить свой более чем скромный опыт в рамках одного проекта -- невероятное оказалось более чем возможным: [ http://code.google.com/p/n2contrib/ ]. Концептуально у меня все очень похоже (терминология, правда, чуть другая). Реализация отталкивается от потребности веб-приложений в декларативном определении и привязки workflow к бизнесс-сущностям, посему API проработан слабо, также, напрочь отсутствует ролевые разграничения и программные обработчики -- из-за этого название пока "human workflow". Зато получился вменяемый GUI для "рисования" workflow. Попытаюсь унифицировать API c Listma.

    ОтветитьУдалить