From 2f9b6cae5dc0fa0e51feeb375188a5c0b20f96cb Mon Sep 17 00:00:00 2001 From: Stephan Baltzer Date: Tue, 12 Nov 2019 18:47:44 +0100 Subject: [PATCH] Elsa.Activities.Reflection --- .../Activities/ExecuteFunction.cs | 117 ++++++++++++++++++ .../Activities/SplitObject.cs | 81 ++++++++++++ .../Elsa.Activities.Reflection.csproj | 26 ++++ .../Extensions/ServiceCollectionExtensions.cs | 15 +++ 4 files changed, 239 insertions(+) create mode 100644 src/activities/Elsa.Activities.Reflection/Activities/ExecuteFunction.cs create mode 100644 src/activities/Elsa.Activities.Reflection/Activities/SplitObject.cs create mode 100644 src/activities/Elsa.Activities.Reflection/Elsa.Activities.Reflection.csproj create mode 100644 src/activities/Elsa.Activities.Reflection/Extensions/ServiceCollectionExtensions.cs diff --git a/src/activities/Elsa.Activities.Reflection/Activities/ExecuteFunction.cs b/src/activities/Elsa.Activities.Reflection/Activities/ExecuteFunction.cs new file mode 100644 index 000000000..68f282c96 --- /dev/null +++ b/src/activities/Elsa.Activities.Reflection/Activities/ExecuteFunction.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Attributes; +using Elsa.Results; +using Elsa.Services; +using Elsa.Services.Models; + +namespace Elsa.Activities.Reflection.Activities +{ + /// + /// Execute a Method by reflection. + /// + [ActivityDefinition( + Category = "Reflection", + Description = "Execute a Method by reflection.", + RuntimeDescription = "a => !!a.state.variableName ? `Execute a Method by reflection and store the result into ${ a.state.variableName }.` : 'Execute a Method by reflection.'", + Outcomes = new[] { OutcomeNames.Done } + )] + public class ExecuteMethod : Activity + { + + public ExecuteMethod(IWorkflowExpressionEvaluator evaluator) + { + + } + + + [ActivityProperty(Hint = "The variables to use as parameters, seperated by comma in order of method call.")] + public string InputVariableNames + { + get => GetState(null, "InputVariableNames"); + set => SetState(value, "InputVariableNames"); + } + + [ActivityProperty(Hint = "The name of the variable to store the returned value into.")] + public string OutputVariableName + { + get => GetState(null, "OutputVariableName"); + set => SetState(value, "OutputVariableName"); + } + + [ActivityProperty(Hint = "Assembly name (fullname or filename) to load")] + public string AssemblyName + { + get => GetState(null, "AssemblyName"); + set => SetState(value, "AssemblyName"); + } + + [ActivityProperty(Hint = "Class name to start or lookup")] + public string ClassName + { + get => GetState(null, "ClassName"); + set => SetState(value, "ClassName"); + } + + [ActivityProperty(Hint = "Class is a static class?")] + public bool IsStaticClass + { + get => GetState(null, "IsStaticClass"); + set => SetState(value, "IsStaticClass"); + } + + [ActivityProperty(Hint = "Method name to execute")] + public string MethodName + { + get => GetState(null, "MethodName"); + set => SetState(value, "MethodName"); + } + + protected override async Task OnExecuteAsync( + WorkflowExecutionContext context, + CancellationToken cancellationToken) + { + await Task.Delay(1); + var inputValues = InputVariableNames.Split(',').Select(s => context.GetVariable(s)).ToArray(); + return Execute(context, inputValues); + } + + protected override ActivityExecutionResult OnResume(WorkflowExecutionContext context) + { + var inputValues = InputVariableNames.Split(',').Select(s => context.GetVariable(s)).ToArray(); + return Execute(context, inputValues); + } + + private ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, object[] receivedInputValues) + { + string path = Directory.EnumerateFiles(AppDomain.CurrentDomain.BaseDirectory, AssemblyName, SearchOption.AllDirectories).FirstOrDefault(); + Assembly assembly = Assembly.LoadFrom(path); + object receivedOutput = null; + var classType = assembly.DefinedTypes.Where(t => t.Name == ClassName).FirstOrDefault(); + + if (IsStaticClass) + { + var staticMethod = classType.DeclaredMethods.Where(m => m.Name == MethodName).FirstOrDefault(); + receivedOutput = staticMethod.Invoke(null, receivedInputValues); + } + else + { + var instancedClass = Activator.CreateInstance(classType); + var instanceMethod = instancedClass.GetType().GetMethod(MethodName); + receivedOutput = instanceMethod.Invoke(instancedClass, receivedInputValues); + } + + workflowContext.CurrentScope.SetVariable(OutputVariableName, receivedOutput); + + workflowContext.SetLastResult(receivedOutput); + + + return Outcome(OutcomeNames.Done); + } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Reflection/Activities/SplitObject.cs b/src/activities/Elsa.Activities.Reflection/Activities/SplitObject.cs new file mode 100644 index 000000000..706bf70b5 --- /dev/null +++ b/src/activities/Elsa.Activities.Reflection/Activities/SplitObject.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Attributes; +using Elsa.Results; +using Elsa.Services; +using Elsa.Services.Models; + +namespace Elsa.Activities.Reflection.Activities +{ + /// + /// Execute a Method by reflection. + /// + [ActivityDefinition( + Category = "Reflection", + Description = "Split object in multiple parts to process seperately.", + RuntimeDescription = "Split object in multiple parts to process seperately.", + Outcomes = "x => x.state.properties.map(c => c.toString())" + )] + public class SplitObject : Activity + { + public SplitObject() + { + Properties = new List(); + } + + [ActivityProperty(Hint = "The variable with the object to split")] + public string InputVariableName + { + get => GetState(null, "InputVariableName"); + set => SetState(value, "InputVariableName"); + } + + [ActivityProperty(Hint = "A comma-separated list of possible outcomes for the split parts. These are the property names of the splitted object.")] + public IReadOnlyCollection Properties + { + get => GetState>(); + set => SetState(value); + } + + protected override async Task OnExecuteAsync( + WorkflowExecutionContext context, + CancellationToken cancellationToken) + { + object splitObject = null; + if (string.IsNullOrEmpty(InputVariableName)) + { + splitObject = context.GetVariable(InputVariableName); + } + + if (splitObject != null) + { + foreach (var Property in Properties) + { + object PropValue = FollowPropertyPath(splitObject, Property); + context.SetVariable(Property, PropValue); + } + } + + return Outcomes(Properties.ToList()); + } + + protected object FollowPropertyPath(object value, string path) + { + Type currentType = value.GetType(); + + foreach (string propertyName in path.Split('.')) + { + PropertyInfo property = currentType.GetProperty(propertyName); + value = property.GetValue(value, null); + currentType = property.PropertyType; + } + return value; + } + + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Reflection/Elsa.Activities.Reflection.csproj b/src/activities/Elsa.Activities.Reflection/Elsa.Activities.Reflection.csproj new file mode 100644 index 000000000..30aee6033 --- /dev/null +++ b/src/activities/Elsa.Activities.Reflection/Elsa.Activities.Reflection.csproj @@ -0,0 +1,26 @@ + + + + netstandard2.0 + 1.0.0 + latest + Elsa Contributors + + Elsa is a set of workflow libraries and tools that enable super-fast workflowing capabilities in any .NET Core application. + This package provides the following Reflection activities: + + * ExecuteFunction + + + 2019 + https://github.com/elsa-workflows/elsa-core + https://github.com/elsa-workflows/elsa-core + GitHub + elsa, workflows + + + + + + + diff --git a/src/activities/Elsa.Activities.Reflection/Extensions/ServiceCollectionExtensions.cs b/src/activities/Elsa.Activities.Reflection/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..d66156cf9 --- /dev/null +++ b/src/activities/Elsa.Activities.Reflection/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,15 @@ +using Elsa.Activities.Reflection.Activities; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Activities.Reflection.Extensions +{ + public static class ServiceCollectionExtensions + { + public static IServiceCollection AddReflectionActivities(this IServiceCollection services) + { + return services + .AddActivity() + .AddActivity(); + } + } +} \ No newline at end of file