Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion WorkflowCore.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.4
VisualStudioVersion = 15.0.26430.6
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF47161E-E399-451C-BDE8-E92AAD3BD761}"
EndProject
Expand Down Expand Up @@ -84,6 +84,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Sample12", "sr
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Tests.Sqlite", "test\WorkflowCore.Tests.Sqlite\WorkflowCore.Tests.Sqlite.csproj", "{F9F8F9CD-01D9-468B-856D-6A87F0762A01}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.LockProviders.SqlServer", "src\providers\WorkflowCore.LockProviders.SqlServer\WorkflowCore.LockProviders.SqlServer.csproj", "{AAE2E9F9-37EF-4AE1-A200-D37417C9040C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -222,6 +224,10 @@ Global
{F9F8F9CD-01D9-468B-856D-6A87F0762A01}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F9F8F9CD-01D9-468B-856D-6A87F0762A01}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F9F8F9CD-01D9-468B-856D-6A87F0762A01}.Release|Any CPU.Build.0 = Release|Any CPU
{AAE2E9F9-37EF-4AE1-A200-D37417C9040C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AAE2E9F9-37EF-4AE1-A200-D37417C9040C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AAE2E9F9-37EF-4AE1-A200-D37417C9040C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AAE2E9F9-37EF-4AE1-A200-D37417C9040C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -263,5 +269,6 @@ Global
{58D0480F-D05D-4348-86D9-B0A7255700E6} = {5080DB09-CBE8-4C45-9957-C3BB7651755E}
{BB776411-D279-419F-8697-5C6F52BCD5CD} = {5080DB09-CBE8-4C45-9957-C3BB7651755E}
{F9F8F9CD-01D9-468B-856D-6A87F0762A01} = {E6CEAD8D-F565-471E-A0DC-676F54EAEDEB}
{AAE2E9F9-37EF-4AE1-A200-D37417C9040C} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2}
EndGlobalSection
EndGlobal
2 changes: 2 additions & 0 deletions src/WorkflowCore/Primitives/SubscriptionStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public override ExecutionPipelineDirective InitForExecution(WorkflowExecutorResu
if (EffectiveDate != null)
effectiveDate = Convert.ToDateTime(EffectiveDate.Compile().DynamicInvoke(workflow.Data));

effectiveDate = effectiveDate.ToUniversalTime();

executionPointer.EventName = EventName;
executionPointer.Active = false;

Expand Down
2 changes: 1 addition & 1 deletion src/WorkflowCore/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public static void AddWorkflow(this IServiceCollection services, Action<Workflow
services.AddTransient<IWorkflowThread, WorkflowThread>();
services.AddTransient<IEventThread, EventThread>();
services.AddTransient<IRunnablePoller, RunnablePoller>();

services.AddTransient<Foreach>();
}
}
Expand Down
20 changes: 19 additions & 1 deletion src/WorkflowCore/Services/WorkflowExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ private void DetermineNextExecutionTime(WorkflowInstance workflow)
{
foreach (var pointer in workflow.ExecutionPointers.Where(x => x.Active && (x.Children ?? new List<string>()).Count > 0))
{
if (workflow.ExecutionPointers.Where(x => pointer.Children.Contains(x.Id)).All(x => x.EndTime != null))
if (workflow.ExecutionPointers.Where(x => pointer.Children.Contains(x.Id)).All(x => IsBranchComplete(workflow.ExecutionPointers, x.Id)))
{
workflow.NextExecution = 0;
return;
Expand All @@ -257,5 +257,23 @@ private void DetermineNextExecutionTime(WorkflowInstance workflow)
}
}

private bool IsBranchComplete(IEnumerable<ExecutionPointer> pointers, string rootId)
{
//TODO: move to own class
var root = pointers.First(x => x.Id == rootId);

if (root.EndTime == null)
return false;

var list = pointers.Where(x => x.PredecessorId == rootId).ToList();

bool result = true;

foreach (var item in list)
result = result && IsBranchComplete(pointers, item.Id);

return result;
}

}
}
6 changes: 3 additions & 3 deletions src/WorkflowCore/WorkflowCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Description>Workflow Core is a light weight workflow engine targeting .NET Standard.</Description>
<Version>1.2.4</Version>
<AssemblyVersion>1.2.4.0</AssemblyVersion>
<FileVersion>1.2.4.0</FileVersion>
<Version>1.2.5</Version>
<AssemblyVersion>1.2.5.0</AssemblyVersion>
<FileVersion>1.2.5.0</FileVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This makes it possible to have a cluster of nodes processing your workflows, alo
Install the NuGet package "WorkflowCore.LockProviders.Redlock"

```
PM> Install-Package WorkflowCore.LockProviders.Redlock -Pre
PM> Install-Package WorkflowCore.LockProviders.Redlock
```

## Usage
Expand Down
20 changes: 20 additions & 0 deletions src/providers/WorkflowCore.LockProviders.SqlServer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# SQL Server DLM provider for Workflow Core

Provides [DLM](https://en.wikipedia.org/wiki/Distributed_lock_manager) support on [Workflow Core](../../README.md) using SQL Server's sp_getapplock.

This makes it possible to have a cluster of nodes processing your workflows, along with a queue provider.

## Installing

Install the NuGet package "WorkflowCore.LockProviders.SqlServer"

```
PM> Install-Package WorkflowCore.LockProviders.SqlServer
```

## Usage

Use the .UseSqlServerLocking extension method when building your service provider.

```C#
services.AddWorkflow(x => x.UseSqlServerLocking("connection string"));
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using WorkflowCore.LockProviders.SqlServer;
using WorkflowCore.Models;

namespace Microsoft.Extensions.DependencyInjection
{
public static class ServiceCollectionExtensions
{
public static WorkflowOptions UseSqlServerLocking(this WorkflowOptions options, string connectionString)
{
options.UseDistributedLockManager(sp => new SqlLockProvider(connectionString, sp.GetService<ILoggerFactory>()));
return options;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System;
using System.Data.SqlClient;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using WorkflowCore.Interface;

namespace WorkflowCore.LockProviders.SqlServer
{
public class SqlLockProvider : IDistributedLockProvider
{
private const string Prefix = "wfc";

private readonly SqlConnection _connection;
private readonly ILogger _logger;

public SqlLockProvider(string connectionString, ILoggerFactory logFactory)
{
_logger = logFactory.CreateLogger<SqlLockProvider>();
var csb = new SqlConnectionStringBuilder(connectionString);
csb.Pooling = false;

_connection = new SqlConnection(csb.ToString());
}


public async Task<bool> AcquireLock(string Id)
{
var cmd = _connection.CreateCommand();
cmd.CommandText = "EXEC @result = sp_getapplock @Resource = @id, @LockMode = 'Exclusive', @LockOwner = 'Session'";
cmd.Parameters.AddWithValue("id", $"{Prefix}:{Id}");
var result = Convert.ToInt32(await cmd.ExecuteScalarAsync());

switch (result)
{
case -1:
_logger.LogDebug($"The lock request timed out for {Id}");
break;
case -2:
_logger.LogDebug($"The lock request was canceled for {Id}");
break;
case -3:
_logger.LogDebug($"The lock request was chosen as a deadlock victim for {Id}");
break;
case -999:
_logger.LogError($"Lock provider error for {Id}");
break;
}

return (result >= 0);
}

public async Task ReleaseLock(string Id)
{
var cmd = _connection.CreateCommand();
cmd.CommandText = "EXEC @result = sp_releaseapplock @Resource = @id, @LockOwner = 'Session'";
cmd.Parameters.AddWithValue("id", $"{Prefix}:{Id}");
var result = Convert.ToInt32(await cmd.ExecuteScalarAsync());

if (result < 0)
_logger.LogError($"Unable to release lock for {Id}");
}

public void Start()
{
_connection.Open();
}

public void Stop()
{
_connection.Close();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<Description>Distributed lock provider for Workflow-core using SQL Server</Description>
<PackageProjectUrl>https://github.com/danielgerlag/workflow-core</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/danielgerlag/workflow-core/blob/master/LICENSE.md</PackageLicenseUrl>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Data.SqlClient" Version="4.3.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\WorkflowCore\WorkflowCore.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.2" />
</ItemGroup>

<ItemGroup>
Expand Down