Llamachant Framework Modules
- Home
- Docs
- Llamachant Framework Modules
- Additional Modules
- Workflow Service Engine
Workflow Service Engine
The Workflow Service Engine is used to process workflow definitions created by our LlamachantFramework.Workflow module. The engine allows you to embed the processing logic into any application. We suggest one of the following:
- Windows Service
- Azure Function (Timer Based)
- Console Application that is invoked by Task Scheduler
Getting Started
- Install the LlamachantFramework.Workflow.Service from NuGet into your solution
- Create an instance of your XAF Application
- Initialize the WorkflowService.Instance
- Configure your custom Email Service (Optional)
- Process the workflows
Sample Azure Function (Using XAF Blazor Application)
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.AspNetCore.DesignTime;
using DevExpress.ExpressApp.Security;
using LlamachantFramework.Workflow.BusinessObjects;
using LlamachantFramework.Workflow.Service;
using LlamachantFramework.Workflow.Utils;
public class RunWorkflowFunction
{
static MyBlazorApplication app;
static IConfigurationRoot config;
[FunctionName("RunWorkflowFunction")]
public void Run([TimerTrigger("0 * * * * *")]TimerInfo myTimer, ILogger log, ExecutionContext context)
{
DevExpress.Utils.AzureCompatibility.Enable = true;
try
{
log.LogInformation("*** Function is starting ***");
if (app == null)
{
log.LogInformation("Application does not exist. Creating application.");
config = new ConfigurationBuilder().SetBasePath(context.FunctionAppDirectory)
#if DEBUG
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
#endif
.AddEnvironmentVariables().Build();
DevExpress.ExpressApp.FrameworkSettings.DefaultSettingsCompatibilityMode = DevExpress.ExpressApp.FrameworkSettingsCompatibilityMode.Latest;
IHostBuilder hostBuilder = Program.CreateHostBuilder(new string[0]);
app = (MyBlazorApplication)DesignTimeApplicationFactoryHelper.Create(hostBuilder);
app.ConnectionString = config.GetConnectionString("ConnectionString");
app.CheckCompatibilityType = CheckCompatibilityType.DatabaseSchema;
app.DatabaseUpdateMode = DatabaseUpdateMode.Never;
app.Setup();
log.LogInformation("Application created and setup complete.");
TemplateFormattingFactory.SetDefaultFormattingType(TemplateFormattingFactory.TemplateFormattingType.DoubleBrace);
//WorkflowService.Instance.EmailService = ...; //Set custom email service here if desired
WorkflowService.Instance.TimeZone = TimeZoneInfo.FindSystemTimeZoneById(config.GetValue("WorkflowTimezone"));
}
if (!app.GetSecurityStrategy().IsAuthenticated)
{
log.LogInformation("Authenticating Workflow User");
IObjectSpace space = app.ObjectSpaceProvider.CreateUpdatingObjectSpace(false);
AuthenticationStandardLogonParameters p = (AuthenticationStandardLogonParameters)app.Security.LogonParameters;
p.UserName = config.GetValue("WorkflowUserName");
p.Password = config.GetValue("WorkflowPassword");
app.GetSecurityStrategy().Logon(space);
log.LogInformation("Workflow User Authenticated Successfully.");
}
if (app.GetSecurityStrategy().IsAuthenticated)
{
log.LogInformation("Workfow Service is Initializing");
WorkflowService.Instance.Initialize(app, logger);
log.LogInformation("Workfow Service is Running");
WorkflowService.Instance.RunWorkflow();
}
log.LogInformation("*** Function is ending ***");
}
catch (Exception ex)
{
log.LogInformation("******** Service Crash ********");
log.LogError(ex, "");
}
}
}
Sample Windows Service
public partial class MyWorkflowService : ServiceBase
{
//You can utilize the built in WorkflowQueueController to handle timing for you
WorkflowQueueController queue = new WorkflowQueueController();
public MyWorkflowService()
{
InitializeComponent();
Tracing.Initialize();
MyWindowsFormsApplication app = new MyWindowsFormsApplication();
app.ConnectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
app.SplashScreen = null;
app.Setup();
IObjectSpace space = app.ObjectSpaceProvider.CreateUpdatingObjectSpace(false);
AuthenticationStandardLogonParameters p = (AuthenticationStandardLogonParameters)app.Security.LogonParameters;
p.UserName = "Admin";
app.GetSecurityStrategy().Logon(space);
WorkflowService.Instance.Initialize(app, new FileSystemLogger(@"C:\Logs\"));
//Set this to your custom IWorkflowEmailService if necessary
//WorkflowService.Instance.EmailService = new DummyEmailService();
}
protected override void OnStart(string[] args)
{
//Set the number of seconds between checks for new workflows (60 seconds recommended)
queue.SetInterval(60);
queue.Start();
}
protected override void OnStop()
{
queue.Stop();
}
}
Sample Console Application
public class MyConsoleApp
{
public static void Main(string[] args)
{
Tracing.Initialize();
LlamaFrameworkTestMyWindowsFormsApplication app = new MyWindowsFormsApplication();
app.ConnectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
app.SplashScreen = null;
app.Setup();
IObjectSpace space = app.ObjectSpaceProvider.CreateUpdatingObjectSpace(false);
AuthenticationStandardLogonParameters p = (AuthenticationStandardLogonParameters)app.Security.LogonParameters;
p.UserName = "Admin";
app.GetSecurityStrategy().Logon(space);
WorkflowService.Instance.Initialize(app, new FileSystemLogger(@"C:\Logs\"));
//Set this to your custom IWorkflowEmailService if necessary
//WorkflowService.Instance.EmailService = new DummyEmailService();
WorkflowService.Instance.RunWorkflow();
}
}
Managing Workflow Definition Dates
Handle the ProgressWorkflowDefinitionDate event in the WorkflowService.Instance to push the NextRunDate field out further. This is useful if you want to skip holidays or weekends.
WorkflowService.Instance.ProgressWorkflowDefinitionDate += (s, args) =>
{
while(e.NextRunDate.DayOfWeek == DayOfWeek.Saturday || e.NextRunDate.DayOfWeek== DayOfWeek.Sunday)
e.NextRunDate = e.NextRunDate.AddDays(1);
};
Calculate if workflow processing is required
WorkflowService.Instance.CalculateNeedsProcessing += (s, args) =>
{
bool blankemail = e.ObjectToProcess is Client c && String.IsNullOrEmpty(c.EmailAddress) && e.WorkflowDefinition.Name == "My Workflow Definition";
e.Process = !blankemail;
};