Skip to content

Commit 801ed09

Browse files
authored
Merge pull request #834 from dwy12321/DeepExpressionProperty
support yaml definition of Outputs with Deep expression property
2 parents 1c199e4 + cab5eae commit 801ed09

File tree

7 files changed

+185
-13
lines changed

7 files changed

+185
-13
lines changed

WorkflowCore.sln

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.QueuePro
150150
EndProject
151151
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample19", "src\samples\WorkflowCore.Sample19\WorkflowCore.Sample19.csproj", "{1223ED47-3E5E-4960-B70D-DFAF550F6666}"
152152
EndProject
153-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Persistence.RavenDB", "src\providers\WorkflowCore.Persistence.RavenDB\WorkflowCore.Persistence.RavenDB.csproj", "{AF205715-C8B7-42EF-BF14-AFC9E7F27242}"
153+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Persistence.RavenDB", "src\providers\WorkflowCore.Persistence.RavenDB\WorkflowCore.Persistence.RavenDB.csproj", "{AF205715-C8B7-42EF-BF14-AFC9E7F27242}"
154154
EndProject
155155
Global
156156
GlobalSection(SolutionConfigurationPlatforms) = preSolution

src/WorkflowCore.DSL/Services/DefinitionLoader.cs

Lines changed: 98 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -217,30 +217,116 @@ private void AttachOutputs(StepSourceV1 source, Type dataType, Type stepType, Wo
217217
var sourceExpr = DynamicExpressionParser.ParseLambda(new[] { stepParameter }, typeof(object), output.Value);
218218

219219
var dataParameter = Expression.Parameter(dataType, "data");
220-
Expression targetProperty;
221220

222-
// Check if our datatype has a matching property
223-
var propertyInfo = dataType.GetProperty(output.Key);
224-
if (propertyInfo != null)
221+
222+
if(output.Key.Contains(".") || output.Key.Contains("["))
223+
{
224+
AttachNestedOutput(output, step, source, sourceExpr, dataParameter);
225+
}else
225226
{
226-
targetProperty = Expression.Property(dataParameter, propertyInfo);
227-
var targetExpr = Expression.Lambda(targetProperty, dataParameter);
228-
step.Outputs.Add(new MemberMapParameter(sourceExpr, targetExpr));
227+
AttachDirectlyOutput(output, step, dataType, sourceExpr, dataParameter);
229228
}
230-
else
229+
}
230+
}
231+
232+
private void AttachDirectlyOutput(KeyValuePair<string, string> output, WorkflowStep step, Type dataType, LambdaExpression sourceExpr, ParameterExpression dataParameter)
233+
{
234+
Expression targetProperty;
235+
236+
// Check if our datatype has a matching property
237+
var propertyInfo = dataType.GetProperty(output.Key);
238+
if (propertyInfo != null)
239+
{
240+
targetProperty = Expression.Property(dataParameter, propertyInfo);
241+
var targetExpr = Expression.Lambda(targetProperty, dataParameter);
242+
step.Outputs.Add(new MemberMapParameter(sourceExpr, targetExpr));
243+
}
244+
else
245+
{
246+
// If we did not find a matching property try to find a Indexer with string parameter
247+
propertyInfo = dataType.GetProperty("Item");
248+
targetProperty = Expression.Property(dataParameter, propertyInfo, Expression.Constant(output.Key));
249+
250+
Action<IStepBody, object> acn = (pStep, pData) =>
251+
{
252+
object resolvedValue = sourceExpr.Compile().DynamicInvoke(pStep); ;
253+
propertyInfo.SetValue(pData, resolvedValue, new object[] { output.Key });
254+
};
255+
256+
step.Outputs.Add(new ActionParameter<IStepBody, object>(acn));
257+
}
258+
259+
}
260+
261+
private void AttachNestedOutput( KeyValuePair<string, string> output, WorkflowStep step, StepSourceV1 source, LambdaExpression sourceExpr, ParameterExpression dataParameter)
262+
{
263+
PropertyInfo propertyInfo = null;
264+
String[] paths = output.Key.Split('.');
265+
266+
Expression targetProperty = dataParameter;
267+
268+
bool hasAddOutput = false;
269+
270+
foreach (String propertyName in paths)
271+
{
272+
if (hasAddOutput)
273+
{
274+
throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}");
275+
}
276+
277+
if (targetProperty == null)
278+
{
279+
break;
280+
}
281+
282+
if (propertyName.Contains("["))
231283
{
232-
// If we did not find a matching property try to find a Indexer with string parameter
233-
propertyInfo = dataType.GetProperty("Item");
234-
targetProperty = Expression.Property(dataParameter, propertyInfo, Expression.Constant(output.Key));
284+
String[] items = propertyName.Split('[');
285+
286+
if (items.Length != 2)
287+
{
288+
throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}");
289+
}
290+
291+
items[1] = items[1].Trim().TrimEnd(']').Trim().Trim('"');
292+
293+
MemberExpression memberExpression = Expression.Property(targetProperty, items[0]);
294+
295+
if (memberExpression == null)
296+
{
297+
throw new ArgumentException($"Unknown property for output {output.Key} on {source.Id}");
298+
}
299+
propertyInfo = ((PropertyInfo)memberExpression.Member).PropertyType.GetProperty("Item");
235300

236301
Action<IStepBody, object> acn = (pStep, pData) =>
237302
{
303+
var targetExpr = Expression.Lambda(memberExpression, dataParameter);
304+
object data = targetExpr.Compile().DynamicInvoke(pData);
238305
object resolvedValue = sourceExpr.Compile().DynamicInvoke(pStep); ;
239-
propertyInfo.SetValue(pData, resolvedValue, new object[] { output.Key });
306+
propertyInfo.SetValue(data, resolvedValue, new object[] { items[1] });
240307
};
241308

242309
step.Outputs.Add(new ActionParameter<IStepBody, object>(acn));
310+
hasAddOutput = true;
243311
}
312+
else
313+
{
314+
try
315+
{
316+
targetProperty = Expression.Property(targetProperty, propertyName);
317+
}
318+
catch
319+
{
320+
targetProperty = null;
321+
break;
322+
}
323+
}
324+
}
325+
326+
if (targetProperty != null && !hasAddOutput)
327+
{
328+
var targetExpr = Expression.Lambda(targetProperty, dataParameter);
329+
step.Outputs.Add(new MemberMapParameter(sourceExpr, targetExpr));
244330
}
245331
}
246332

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System;
2+
3+
namespace WorkflowCore.TestAssets.DataTypes
4+
{
5+
public class CounterBoardWithDynamicData: CounterBoard
6+
{
7+
public DynamicData DynamicDataInstance { get; set; }
8+
public CounterBoard CounterBoardInstance { get; set; }
9+
}
10+
}

test/WorkflowCore.TestAssets/Utils.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ public static string GetTestDefinitionDynamicJson()
3030
{
3131
return File.ReadAllText("stored-dynamic-definition.json");
3232
}
33+
public static string GetTestDefinitionDynamicYaml()
34+
{
35+
return File.ReadAllText("stored-dynamic-definition.yaml");
36+
}
3337

3438
public static string GetTestDefinitionJsonMissingInputProperty()
3539
{

test/WorkflowCore.TestAssets/WorkflowCore.TestAssets.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313
<None Remove="stored-definition.json" />
1414
<None Remove="stored-definition.yaml" />
1515
<None Remove="stored-dynamic-definition.json" />
16+
<None Remove="stored-dynamic-definition.yaml" />
1617
</ItemGroup>
1718

1819
<ItemGroup>
20+
<EmbeddedResource Include="stored-dynamic-definition.yaml">
21+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
22+
</EmbeddedResource>
1923
<EmbeddedResource Include="stored-definition.yaml">
2024
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
2125
</EmbeddedResource>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
Id: Test
2+
Version: 1
3+
DataType: WorkflowCore.TestAssets.DataTypes.CounterBoardWithDynamicData, WorkflowCore.TestAssets
4+
Steps:
5+
- Id: Step1
6+
StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets
7+
ErrorBehavior: Retry
8+
Inputs:
9+
Value: data.DynamicDataInstance["test"]
10+
Outputs:
11+
DynamicDataInstance["test"]: step.Value
12+
NextStepId: Step2
13+
- Id: Step2
14+
StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets
15+
Inputs:
16+
Value: data.Counter2
17+
Outputs:
18+
CounterBoardInstance.Counter1: step.Value
19+
NextStepId: Step3
20+
- Id: Step3
21+
StepType: WorkflowCore.Primitives.If, WorkflowCore
22+
NextStepId: Step4
23+
Inputs:
24+
Condition: object.Equals(data.Flag1, true)
25+
Do:
26+
- - Id: Step3.1.1
27+
StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets
28+
Inputs:
29+
Value: data.Counter3
30+
Outputs:
31+
Counter3: step.Value
32+
NextStepId: Step3.1.2
33+
- Id: Step3.1.2
34+
StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets
35+
Inputs:
36+
Value: data.Counter4
37+
Outputs:
38+
Counter4: step.Value
39+
- - Id: Step3.2.1
40+
StepType: WorkflowCore.Primitives.WaitFor, WorkflowCore
41+
NextStepId: Step3.2.2
42+
CancelCondition: data.Flag2
43+
Inputs:
44+
EventName: '"Event1"'
45+
EventKey: '"Key1"'
46+
EffectiveDate: DateTime.Now
47+
- Id: Step3.2.2
48+
StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets
49+
Inputs:
50+
Value: data.Counter5
51+
Outputs:
52+
Counter5: step.Value
53+
- Id: Step4
54+
StepType: WorkflowCore.TestAssets.Steps.Counter, WorkflowCore.TestAssets
55+
Inputs:
56+
Value: data.Counter6
57+
Outputs:
58+
Counter6: step.Value

test/WorkflowCore.UnitTests/Services/DefinitionStorage/DefinitionLoaderTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ public void ParseDefinition()
4343
A.CallTo(() => _registry.RegisterWorkflow(A<WorkflowDefinition>.That.Matches(MatchTestDefinition, ""))).MustHaveHappened();
4444
}
4545

46+
[Fact(DisplayName = "Should parse definition")]
47+
public void ParseDefinitionPropertyDynamic()
48+
{
49+
_subject.LoadDefinition(TestAssets.Utils.GetTestDefinitionDynamicYaml(), Deserializers.Yaml);
50+
51+
A.CallTo(() => _registry.RegisterWorkflow(A<WorkflowDefinition>.That.Matches(x => x.Id == "Test"))).MustHaveHappened();
52+
A.CallTo(() => _registry.RegisterWorkflow(A<WorkflowDefinition>.That.Matches(x => x.Version == 1))).MustHaveHappened();
53+
A.CallTo(() => _registry.RegisterWorkflow(A<WorkflowDefinition>.That.Matches(x => x.DataType == typeof(CounterBoardWithDynamicData)))).MustHaveHappened();
54+
A.CallTo(() => _registry.RegisterWorkflow(A<WorkflowDefinition>.That.Matches(MatchTestDefinition, ""))).MustHaveHappened();
55+
}
4656

4757
[Fact(DisplayName = "Should parse definition")]
4858
public void ParseDefinitionDynamic()

0 commit comments

Comments
 (0)