Para baixar o código fonte do seguinte artigo acesse o Codeplex.

Introdução

O objetivo desse artigo é demonstrar o controle da navegação com Workflow Foundation 4.0 em uma aplicação ASP.NET MVC e como persistir este Workflow numa base SQL Server.

Requisitos

Usaremos nesse artigo:

  • O Visual Studio 2012
  • Um projeto ASP.NET MVC 4.0
  • Os assemblies System.Runtime.DurableInstancing.dll, System.Activities.DurableInstancing.dll e System.Activities.dll
  • SQL Server 2005 Express ou superior

Antes de desenvolvermos a aplicação vamos criar a base de dados. Precisamos executar alguns scripts para criação das tabelas e procedures que o WF utiliza para persistir os dados. Execute os seguintes scripts na sua base:

  • C:\Windows\Microsoft.NET\Framework\v4.0.30319\SQL\en\SqlWorkflowInstanceStoreSchema.sql
  • C:\Windows\Microsoft.NET\Framework\v4.0.30319\SQL\en\SqlWorkflowInstanceStoreLogic.sql

Este artigo utiliza uma tabela com artigos e usa dois campos WorkflowId e State para fazer a integração com o WF. Este é o script da tabela:

CREATE TABLE [dbo].[Articles](
	[ArticleID] [int] IDENTITY(1,1) NOT NULL,
	[Title] [nvarchar](max) NOT NULL,
	[Published] [bit] NOT NULL,
	[WorkflowID] [uniqueidentifier] NULL,
	[State] [varchar](50) NOT NULL,
 CONSTRAINT [PK_Articles] PRIMARY KEY CLUSTERED 
(
	[ArticleID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Esta é a tabela:

Desenvolvimento

Abaixo o Workflow desse tutorial:

Tenha especial atenção para a classe WorkflowHost.cs que está na pasta Models. Vamos analisa-la em detalhes nesse artigo:

public class WorkflowHost
{
    private WorkflowApplication app;
    private AutoResetEvent instanceUnloaded = new AutoResetEvent(false);

    public WorkflowHost(System.Activities.Activity activity)
    {
        var store = new SqlWorkflowInstanceStore(System.Configuration.ConfigurationManager.ConnectionStrings["WorkflowConnection"].ConnectionString);
        store.InstanceCompletionAction = InstanceCompletionAction.DeleteNothing;

        app = new WorkflowApplication(activity);
        app.InstanceStore = store;
        app.PersistableIdle = (e) =>
        {
            return PersistableIdleAction.Persist;
        };
        app.Completed = (e) =>
        {
            instanceUnloaded.Set();
        };
        app.Idle = (e) =>
        {
            instanceUnloaded.Set();
        };
    }

    public Guid Start()
    {
        app.Run();
        instanceUnloaded.WaitOne();

        return app.Id;
    }

    public void Run(Guid instanceId, object value)
    {
        string bookmarkName = app.GetBookmarks()[0].BookmarkName;
        var book = app.ResumeBookmark(bookmarkName, value);
        instanceUnloaded.WaitOne();
    }

    public void Resume(Guid instanceId)
    {
        app.Load(instanceId);
    }

    public void Unload()
    {
        app.Unload();
    }
}

Já a classe abaixo executa a operação mas cria também um bookmark. Que permite que o workflow seja pausado nessa etapa e reiniciado posteriormente.

Quando criamos esse Bookmark somos orientados a definir a propriedade CanInduceIdle como true, e quando ela é definida como true. O nosso host irá se encarregar de persistir o processo.

public sealed class Evaluate : NativeActivity
{
    public InArgument ArticleID { get; set; }
    public OutArgument Operation { get; set; }

    protected override void Execute(NativeActivityContext context)
    {
        int articleID = context.GetValue(ArticleID);

        DataContext db = new DataContext();
        Article article = db.Articles.Single(e => e.ArticleID == articleID);
        article.State = "Evaluate";

        db.SaveChanges();

        context.CreateBookmark("Evaluate", new BookmarkCallback(this.Continue));
    }

    protected override bool CanInduceIdle { get { return true; } }

    void Continue(NativeActivityContext context, Bookmark bookmark, object obj)
    {
        Operation.Set(context, obj);
    }
}


As atividades Published e Retained fazem parte do workflow e permitem que o workflow continue e como são as últimas etapas, o workflow é finalizado.

public sealed class Published : NativeActivity
{
    public InArgument ArticleID { get; set; }

    protected override void Execute(NativeActivityContext context)
    {
        DataContext db = new DataContext();

        int articleID = context.GetValue(ArticleID);

        Article article = db.Articles.Single(e => e.ArticleID == articleID);

        article.Published = true;
        article.State = "Published";

        db.SaveChanges();
    }
}
public sealed class Retained : NativeActivity
{
    public InArgument ArticleID { get; set; }

    protected override void Execute(NativeActivityContext context)
    {
        DataContext db = new DataContext();

        int articleID = context.GetValue(ArticleID);

        Article article = db.Articles.Single(e => e.ArticleID == articleID);
        article.State = "Retained";

        db.SaveChanges();
    }
}

Conclusão

Este artigo demonstra de forma simples e prática a integração do Workflow Foundation numa aplicação ASP.NET MVC. É um exemplo enxuto e que pode ser extendido de acordo com as necessidades do programador.