Skip to content

Commit e183903

Browse files
authored
Merge pull request #250 from MarioAndron/master
Enhanced to allow scoped dependencies to be injected into steps
2 parents e432405 + 8df0cee commit e183903

File tree

8 files changed

+354
-44
lines changed

8 files changed

+354
-44
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
3+
namespace WorkflowCore.Interface
4+
{
5+
/// <remarks>
6+
/// The implemention of this interface will be responsible for
7+
/// providing a new service scope for a DI container
8+
/// </remarks>
9+
public interface IScopeProvider
10+
{
11+
/// <summary>
12+
/// Create a new service scope
13+
/// </summary>
14+
/// <returns></returns>
15+
IServiceScope CreateScope();
16+
}
17+
}

src/WorkflowCore/ServiceCollectionExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public static IServiceCollection AddWorkflow(this IServiceCollection services, A
4646

4747
services.AddSingleton<IWorkflowController, WorkflowController>();
4848
services.AddSingleton<IWorkflowHost, WorkflowHost>();
49+
services.AddTransient<IScopeProvider, ScopeProvider>();
4950
services.AddTransient<IWorkflowExecutor, WorkflowExecutor>();
5051
services.AddTransient<ICancellationProcessor, CancellationProcessor>();
5152
services.AddTransient<IWorkflowBuilder, WorkflowBuilder>();
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using System;
3+
using WorkflowCore.Interface;
4+
5+
namespace WorkflowCore.Services
6+
{
7+
/// <summary>
8+
/// A concrete implementation for the IScopeProvider interface
9+
/// Largely to get around the problems of unit testing an extension method (CreateScope())
10+
/// </summary>
11+
public class ScopeProvider : IScopeProvider
12+
{
13+
private readonly IServiceProvider provider;
14+
15+
public ScopeProvider(IServiceProvider provider)
16+
{
17+
this.provider = provider;
18+
}
19+
20+
public IServiceScope CreateScope()
21+
{
22+
return provider.CreateScope();
23+
}
24+
}
25+
}

src/WorkflowCore/Services/WorkflowExecutor.cs

Lines changed: 47 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public class WorkflowExecutor : IWorkflowExecutor
1414
{
1515
protected readonly IWorkflowRegistry _registry;
1616
protected readonly IServiceProvider _serviceProvider;
17+
protected readonly IScopeProvider _scopeProvider;
1718
protected readonly IDateTimeProvider _datetimeProvider;
1819
protected readonly ILogger _logger;
1920
private readonly IExecutionResultProcessor _executionResultProcessor;
@@ -23,9 +24,10 @@ public class WorkflowExecutor : IWorkflowExecutor
2324

2425
private IWorkflowHost Host => _serviceProvider.GetService<IWorkflowHost>();
2526

26-
public WorkflowExecutor(IWorkflowRegistry registry, IServiceProvider serviceProvider, IDateTimeProvider datetimeProvider, IExecutionResultProcessor executionResultProcessor, ILifeCycleEventPublisher publisher, ICancellationProcessor cancellationProcessor, WorkflowOptions options, ILoggerFactory loggerFactory)
27+
public WorkflowExecutor(IWorkflowRegistry registry, IServiceProvider serviceProvider, IScopeProvider scopeProvider, IDateTimeProvider datetimeProvider, IExecutionResultProcessor executionResultProcessor, ILifeCycleEventPublisher publisher, ICancellationProcessor cancellationProcessor, WorkflowOptions options, ILoggerFactory loggerFactory)
2728
{
2829
_serviceProvider = serviceProvider;
30+
_scopeProvider = scopeProvider;
2931
_registry = registry;
3032
_datetimeProvider = datetimeProvider;
3133
_publisher = publisher;
@@ -87,57 +89,60 @@ public async Task<WorkflowExecutorResult> Execute(WorkflowInstance workflow)
8789
pointer.StartTime = _datetimeProvider.Now.ToUniversalTime();
8890
}
8991

90-
_logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id);
92+
using (var scope = _scopeProvider.CreateScope())
93+
{
94+
_logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id);
9195

92-
IStepBody body = step.ConstructBody(_serviceProvider);
96+
IStepBody body = step.ConstructBody(scope.ServiceProvider);
9397

94-
if (body == null)
95-
{
96-
_logger.LogError("Unable to construct step body {0}", step.BodyType.ToString());
97-
pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval);
98-
wfResult.Errors.Add(new ExecutionError()
98+
if (body == null)
9999
{
100-
WorkflowId = workflow.Id,
101-
ExecutionPointerId = pointer.Id,
102-
ErrorTime = _datetimeProvider.Now.ToUniversalTime(),
103-
Message = String.Format("Unable to construct step body {0}", step.BodyType.ToString())
104-
});
105-
continue;
106-
}
100+
_logger.LogError("Unable to construct step body {0}", step.BodyType.ToString());
101+
pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval);
102+
wfResult.Errors.Add(new ExecutionError()
103+
{
104+
WorkflowId = workflow.Id,
105+
ExecutionPointerId = pointer.Id,
106+
ErrorTime = _datetimeProvider.Now.ToUniversalTime(),
107+
Message = String.Format("Unable to construct step body {0}", step.BodyType.ToString())
108+
});
109+
continue;
110+
}
107111

108-
IStepExecutionContext context = new StepExecutionContext()
109-
{
110-
Workflow = workflow,
111-
Step = step,
112-
PersistenceData = pointer.PersistenceData,
113-
ExecutionPointer = pointer,
114-
Item = pointer.ContextItem
115-
};
112+
IStepExecutionContext context = new StepExecutionContext()
113+
{
114+
Workflow = workflow,
115+
Step = step,
116+
PersistenceData = pointer.PersistenceData,
117+
ExecutionPointer = pointer,
118+
Item = pointer.ContextItem
119+
};
116120

117-
foreach (var input in step.Inputs)
118-
input.AssignInput(workflow.Data, body, context);
121+
foreach (var input in step.Inputs)
122+
input.AssignInput(workflow.Data, body, context);
119123

120124

121-
switch (step.BeforeExecute(wfResult, context, pointer, body))
122-
{
123-
case ExecutionPipelineDirective.Defer:
124-
continue;
125-
case ExecutionPipelineDirective.EndWorkflow:
126-
workflow.Status = WorkflowStatus.Complete;
127-
workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime();
128-
continue;
129-
}
125+
switch (step.BeforeExecute(wfResult, context, pointer, body))
126+
{
127+
case ExecutionPipelineDirective.Defer:
128+
continue;
129+
case ExecutionPipelineDirective.EndWorkflow:
130+
workflow.Status = WorkflowStatus.Complete;
131+
workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime();
132+
continue;
133+
}
130134

131-
var result = await body.RunAsync(context);
135+
var result = await body.RunAsync(context);
132136

133-
if (result.Proceed)
134-
{
135-
foreach (var output in step.Outputs)
136-
output.AssignOutput(workflow.Data, body, context);
137-
}
137+
if (result.Proceed)
138+
{
139+
foreach (var output in step.Outputs)
140+
output.AssignOutput(workflow.Data, body, context);
141+
}
138142

139-
_executionResultProcessor.ProcessExecutionResult(workflow, def, pointer, step, result, wfResult);
140-
step.AfterExecute(wfResult, context, result, pointer);
143+
_executionResultProcessor.ProcessExecutionResult(workflow, def, pointer, step, result, wfResult);
144+
step.AfterExecute(wfResult, context, result, pointer);
145+
}
141146
}
142147
catch (Exception ex)
143148
{

0 commit comments

Comments
 (0)