Este artículo detalla rápidamente los pasos que debe realizar para habilitar el soporte para Windows Workflow Foundation (WF) en ASP.NET (clásico o MVC). Usemos una aplicación de demostración muy pequeña para mostrarle cómo puede hacer esto.
La integración de WF en ASP.NET es algo que tuve que hacer en los últimos proyectos en los que trabajé. Así que pensé en construir una pequeña aplicación de muestra que le muestre el mínimo de código requerido para lograrlo. Construyamos una aplicación de muestra paso a paso ...
Observación : Este artículo no es un manual en WF. Se requiere alguna comprensión básica de WF.
Inicie SQL Server Management Studio y cree una nueva base de datos llamada "Flujo de trabajo". Debe crear algunas tablas y lógica (procedimientos almacenados y tal) para apoyar la persistencia de WF. En el directorio "C: Windows Microsoft.net Framework V3.0 Windows Workflow Foundation SQL en" encontrará 4 archivos SQL. Ejecute los dos siguientes en este orden:
Esto crea dos tablas y 10 procedimientos almacenados que se requieren para respaldar la persistencia para WF en una base de datos de SQL Server. Ahora descargue el código fuente para este artículo, extraiga y ejecute el script ddl.sql. Este guión crea una tabla llamada Cliente.
Debe terminar con una estructura de base de datos que se vea así:
Figura 1 - Base de datos de flujo de trabajo

Cuando abra la solución de muestra de este artículo, encontrará un proyecto de biblioteca de código llamado "base de datos". Todo lo que contiene es un archivo de clases LINQ to SQL que contiene una entidad, a saber, el cliente.
Figura 2 - Entidad del cliente

La aplicación de muestra le permite crear clientes. Todo lo que tienes que hacer es ingresar un nombre de usuario. Luego se inicia un nuevo flujo de trabajo y puede optar por aprobar al cliente, rechazarlo o no hacer nada. Si no toma medidas, el cliente será eliminado después de un cierto tiempo de espera.
Figura 3 - El flujo de trabajo

En esta muestra hay un flujo de trabajo de la máquina de estado. Para tal flujo de trabajo, debe proporcionar un estado inicial y completo. Como puede ver en la figura anterior, solo contiene dos estados que se nombran adecuadamente.
Este flujo de trabajo requiere un parámetro llamado nombre de usuario. Cuando llega al estado inicial, se ejecuta la Actividad de la Estatalización y se crea y persiste un nuevo cliente en la tabla de clientes. Por defecto, todos los clientes requieren aprobación.
Figura 4 - clientes que esperan aprobación

Puse este flujo de trabajo en un proyecto separado.
Figura 5 - La solución

Cuando la Actividad de la Estado del Estado haya terminado de ejecutar el flujo de trabajo, escuchará uno de los 3 eventos posibles:
El evento de tiempo de espera se basa en una demora activa. No tiene que despedir este evento usted mismo, sin embargo, está a cargo de despedir los otros dos eventos. Por lo tanto, creé un servicio local.
Listado 1 - Interfaz IcustomerService
[ ExternalDataExchange ]
public interface ICustomerService
{
event EventHandler < ExternalDataEventArgs > Approved ;
event EventHandler < ExternalDataEventArgs > Rejected ;
}Solo puede registrar una implementación para un servicio local en el tiempo de ejecución del flujo de trabajo y la implementación para el servicio de clientes se ve así:
Listado 2 - ClientService
public class CustomerService : ICustomerService
{
public event EventHandler < ExternalDataEventArgs > Approved ;
public event EventHandler < ExternalDataEventArgs > Rejected ;
private bool FireEvent ( EventHandler < ExternalDataEventArgs > theEvent , ExternalDataEventArgs args )
{ //...}
public bool OnApproved ( Guid instanceId )
{
return FireEvent ( Approved , new ExternalDataEventArgs ( instanceId ) ) ;
}
public bool OnRejected ( Guid instanceId )
{
return FireEvent ( Rejected , new ExternalDataEventArgs ( instanceId ) ) ;
}
}Este servicio local le permite activar el evento aprobado o rechazado especificando el ID de instancia (GUID) del flujo de trabajo que desea continuar ejecutando.
No hay mucha lógica involucrada en la creación, aprobación o rechazo de clientes, pero he puesto todo en esto en la clase Customermanager. Los métodos se explican por sí mismos. Esta clase le permite recuperar una lista de todos los clientes, agregar nuevos clientes, aprobar, rechazar y eliminar clientes.
Listado 3 - CustomerManager Clase
public class CustomerManager
{
public IEnumerable < Customer > GetCustomers ( ) { //... }
public Customer GetCustomerById ( Guid customerId ) { //... }
public void ApproveCustomer ( Guid customerId ) { //... }
public void RejectCustomer ( Guid customerId ) { //... }
public void DeleteCustomer ( Guid customerId ) { //... }
public void AddCustomer ( Guid customerId , string userName ) { //... }
}El flujo de trabajo utiliza este tipo Customermanager para realizar todo su trabajo.
Voila, se colocan los cimientos. Ha configurado una base de datos que admite la persistencia de WF, creó un flujo de trabajo para crear, aprobar y rechazar a los clientes y crear un servicio local que lo ayudará a dirigir las instancias de su flujo de trabajo qué hacer.
Ahora, deja crear una nueva aplicación web ASP.NET que ponga todo esto a funcionar. Se me ocurrió el nombre original "Aplicación web" para el proyecto ASP.NET.
Figura 6 - Proyecto de aplicación web

El sitio solo contiene una página llamada default.aspx. Esta página muestra una lista de clientes, le permite agregar nuevos clientes y aprobar o rechazar a los existentes.
Figura 7 - Página predeterminada.aspx

Configuremos la aplicación web para que admita WF. El código de configuración que se muestra en este artículo se abrevia para legibilidad. Descargue el código fuente de muestra para la versión completa.
Abra el archivo de configuración de su aplicación (web.config) y agregue la siguiente línea a la sección ConfigSections.
< configSections >
<!-- ... -->
< section name = " WorkflowRuntime "
type = " System.Workflow.Runtime.Configuration.WorkflowRuntimeSection...etc. " />
</ configSections >Debe agregar esta línea al nodo configSections para que .NET sepa que debe usar el tipo de flujo de trabajo de trabajo para leer el siguiente bit de configuración que debe agregar al nodo de configuración.
< WorkflowRuntime >
< CommonParameters >
< add name = " ConnectionString " value = " your connection string " />
</ CommonParameters >
< Services >
< add type = " System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService,...etc. "
useActiveTimers = " true " />
< add type = " System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService,...etc. "
UnloadOnIdle = " true " LoadIntervalSeconds = " 5 " />
</ Services >
</ WorkflowRuntime >El nodo Workflowruntime carga el ManualworkFlowsChedulerService y SQLWorkFlowPersistenceService Services para el tiempo de ejecución de flujo de trabajo. Debe cargar el SQLWorkFlowPersistenceService si desea persistir sus flujos de trabajo y para ASP.NET, es aconsejable utilizar el servicio ManualworkFlowsChedulerService en lugar del servicio predeterminado de OwardFlowsChedulerService.
Cada instancia de flujo de trabajo se ejecuta en un hilo separado. El tiempo de ejecución del flujo de trabajo lo administra utilizando un servicio de programador de flujo de trabajo. Por defecto, el tiempo de ejecución usa el tipo de servicio predeterminado de FlowsChedulerService. Todas las instancias de flujo de trabajo se ejecutan de manera asincrónica.
Para ASP.NET, es mejor usar el ManualworkFlowsChedulerService, porque le permite ejecutar flujos de trabajo sincrónicamente. El tiempo de ejecución del flujo de trabajo utiliza el hilo de llamada de la aplicación host en este caso.
Esto le permite esperar una respuesta del flujo de trabajo antes de que ASP.NET envíe una respuesta. Por supuesto, no es prudente ejecutar tareas de larga duración en sus flujos de trabajo en este caso. (PD: Consulte esta publicación de blog para obtener más información sobre este tema).
Al usar el ManualworkFlowsChedulerService, asegúrese de establecer la propiedad UseActivetimers en verdad. Si deja esta configuración establecida en su valor predeterminado de falso, entonces cualquier demora, no se reanudará automáticamente después de haber expirado. Si se establece en True, el tiempo de ejecución del flujo de trabajo utilizará un temporizador en memoria para verificar periódicamente las retiradas caducadas.
Ahora que su aplicación se ha configurado para Usar Workflow Foundation, debe alojar el tiempo de ejecución del flujo de trabajo.
En ASP.NET, el lugar ideal para hacerlo está en el archivo global.asax. Simplemente agregue el siguiente código al controlador de eventos Application_Start (...).
Listado 4 - Global.asax Application_Start
protected void Application_Start ( object sender , EventArgs e )
{
WorkflowRuntime runtime = new WorkflowRuntime ( "WorkflowRuntime" ) ;
ExternalDataExchangeService exchangeService = new ExternalDataExchangeService ( ) ;
runtime . AddService ( exchangeService ) ;
CustomerService customerService = new CustomerService ( ) ;
exchangeService . AddService ( customerService ) ;
runtime . StartRuntime ( ) ;
Application [ "WorkflowRuntime" ] = runtime ;
}Se crea una nueva instancia de Workflowruntime, luego se crea el ExternalDataExchangeService y se agrega al tiempo de ejecución. A continuación, creamos una instancia de nuestro servicio local Icustomerservice y la agregamos a la instancia de ExternalDataExchangeService. Finalmente, el tiempo de ejecución se inicia y se almacena en el estado de la aplicación.
Del mismo modo, debe detener el tiempo de ejecución del flujo de trabajo cuando la aplicación finaliza como se muestra en el Listado 5.
Listado 5 - Global.asax Application_end
protected void Application_End ( object sender , EventArgs e )
{
WorkflowRuntime runtime = Application [ "WorkflowRuntime" ] as WorkflowRuntime ;
if ( runtime != null )
{
runtime . StopRuntime ( ) ;
runtime . Dispose ( ) ;
}
} Cuando hace clic en el botón Agregar, debe iniciar un nuevo flujo de trabajo. Al usar el ManualworkFlowsChedulerService, usted tiene el control total de ejecutar su flujo de trabajo. En el Listado 6 puede ver que debe seguir estos pasos:
Listado 6 - Crea un nuevo cliente
WorkflowRuntime runtime = ( WorkflowRuntime ) Application [ "WorkflowRuntime" ] ;
Dictionary < string , object > arguments = new Dictionary < string , object > ( ) ;
arguments . Add ( "UserName" , txtUserName . Text . Trim ( ) ) ;
WorkflowInstance workflow = runtime . CreateWorkflow ( typeof ( CustomerApprovalWorkflow ) , arguments ) ;
workflow . Start ( ) ;
ManualWorkflowSchedulerService scheduler =
( ManualWorkflowSchedulerService ) runtime . GetService ( typeof ( ManualWorkflowSchedulerService ) ) ;
scheduler . RunWorkflow ( workflow . InstanceId ) ; Después de haber creado un cliente, puede aprobar o rechazar su cuenta. En otras palabras, debe continuar con el flujo de trabajo que se inició cuando creó su cuenta.
El código que se muestra en el Listado 7 es similar al del Listado 6. La única diferencia es que debe recuperar la instancia de su servicio local (ClientService) y llamar a su método apropiado (...). El método Onapproved desencadena el evento aprobado. Tras una entrega exitosa, puede instruir al planificador que continúe ejecutando el flujo de trabajo.
Listado 7 : apruebe a un cliente
WorkflowRuntime runtime = ( WorkflowRuntime ) Application [ "WorkflowRuntime" ] ;
ManualWorkflowSchedulerService scheduler =
( ManualWorkflowSchedulerService ) runtime . GetService ( typeof ( ManualWorkflowSchedulerService ) ) ;
CustomerService customerService = ( CustomerService ) runtime . GetService < ICustomerService > ( ) ;
if ( customerService . OnApproved ( customerId ) )
{
scheduler . RunWorkflow ( customerId ) ;
}Rechazar la cuenta de un cliente es similar. Solo necesita enviar el evento rechazado en lugar del evento aprobado. Si no toma medidas, la demora activa se activará después de un minuto y la cuenta del cliente se eliminará automáticamente.
Y eso es todo lo que hay.