public void ExpressionWithMultipleKnownEntities() { var entity1 = new Resource(); var entity2 = new Resource(); var expression = new WfExpression { ExpressionString = null }; expression.WfExpressionKnownEntities.Add(new NamedReference { Name = "1", ReferencedEntity = entity1 }); expression.WfExpressionKnownEntities.Add(new NamedReference { Name = "2", ReferencedEntity = entity2 }); expression.ArgumentToPopulate = new ResourceArgument().As <ActivityArgument>(); Workflow wf = new Workflow(); wf.ExpressionMap.Add(expression); var result = ExpressionHelper.EvaluateExpression(expression, TestRunState.CreateDummyState(wf)); Assert.That(result, Is.Not.Null); Assert.That(result, Is.AssignableTo(typeof(IEntity))); Assert.That(((IEntity)result).Id, Is.EqualTo(entity1.Id)); }
/// <summary> /// Given a text expression and runtimeArgs, evaluate the expression. /// Expressions must be compatible with DataColumn.Expression. the result is cast to T /// </summary> public static object EvaluateExpression(this WfExpression expression, IRunState run) { // Evaluate try { var knownEntities = new Dictionary <string, Resource>(); using (new SecurityBypassContext()) { // we need to fetch the known entities in a bypass so that the expression // evaluation will trigger a security error rather than a missing entity. foreach (var ke in expression.WfExpressionKnownEntities) { if (ke.ReferencedEntity != null && !string.IsNullOrEmpty(ke.Name)) { if (knownEntities.ContainsKey(ke.Name)) { knownEntities[ke.Name] = ke.ReferencedEntity; } else { knownEntities.Add(ke.Name, ke.ReferencedEntity); } } } } var result = EvaluateExpressionImpl(expression, run, knownEntities); return(result); } catch (ParseException ex) { throw new WorkflowExpressionEvaluationException(ex.ShortMessage, ex.ToString(), ex); } catch (Exception ex) { var sb = new StringBuilder(); // Format runtimeArgs sb.AppendFormat("Error: {0}\n", ex.Message); sb.AppendFormat("Expression: ** {0} **\n", expression.ExpressionString ?? "null"); sb.AppendFormat("Exception: {0}\n", ex.GetType().Name); sb.AppendFormat("Expression ID: {0}\n", expression.Id); sb.AppendFormat("isTemplate: {0}\n", expression.IsTemplateString == null ? "null" : expression.IsTemplateString.ToString()); sb.AppendFormat("target: {0}\n", expression.ArgumentToPopulate != null ? expression.ArgumentToPopulate.Id.ToString() : "null"); sb.AppendFormat("parameters: \n"); run.PrettyPrint(sb); var activity = expression.ExpressionInActivity; var workflowRunId = activity != null && activity.ContainingWorkflow != null ? activity.ContainingWorkflow.Id : 0; var message = ex is PlatformSecurityException ? $"Expression failed with a security violation" : $"Expression failed during evaluation"; throw new WorkflowExpressionEvaluationException($"{message}: '{expression.ExpressionString ?? "Null"}'", sb.ToString(), ex); } }
public ActivityArgument GetArgumentPopulatedByExpression(WfExpression wfExpr) { ActivityArgument result; if (!_expressionArgumentCache.TryGetValue(wfExpr.Id, out result)) { throw new ArgumentException("Attempted to get an argument for an expression that was not added to the cache."); } return(result); }
public static Workflow AddEntityExpression(this Workflow wf, WfActivity activity, ActivityArgument arg, EntityRef entityRef) { var r = Entity.Get(entityRef).As <Resource>(); var exp = new WfExpression { ArgumentToPopulate = arg, ExpressionInActivity = activity, ExpressionString = string.Format("[{0}]", r.Id.ToString()) }; exp.WfExpressionKnownEntities.Add(new NamedReference { Name = r.Id.ToString(), ReferencedEntity = r }); activity.ExpressionMap.Add(exp); return(wf); }
private void AddExpressionToCaches(WfExpression wfExpr) { var argToPopulate = wfExpr.ArgumentToPopulate; if (argToPopulate != null) { _expressionArgumentCache.TryAdd(wfExpr.Id, argToPopulate); _expressionArgumentTypeCache.TryAdd(argToPopulate.Id, CalcExpressionType(argToPopulate)); var expression = CompileExpression(wfExpr); CompiledExpressionCache.TryAdd(wfExpr.Id, expression); } }
public void ExpressionWithNoKnownEntities() { var expression = new WfExpression { ExpressionString = null }; expression.ArgumentToPopulate = new ResourceArgument().As <ActivityArgument>(); Workflow wf = new Workflow(); wf.ExpressionMap.Add(expression); var result = ExpressionHelper.EvaluateExpression(expression, TestRunState.CreateDummyState(wf)); Assert.That(result, Is.Null); }
string GenerateCompilationError(string messageStart, WfExpression expression, Exception ex) { var errorMessage = ex is ParseException ? ex.Message : "Internal error"; var result = string.Format( "{0} Message: '{1}' Expression: '{2}'", messageStart, errorMessage, expression.ExpressionString ?? "[Empty]" ); if (!(ex is ParseException)) { EventLog.Application.WriteError("Unexpected error during workflow expression compilation. {0} \nInternal message {1}", result, ex.Message); } return(result); }
public static void SetActivityArgumentToResource(Workflow wf, WfActivity activity, string argumentName, Resource resource) { var arg = activity.GetInputArgument(argumentName); if (arg == null) { throw new ArgumentException(String.Format("No matching argument name for activity. Activity='{0}' Argument='{1}'", activity.Name ?? "<unnamed>", argumentName ?? "<unnamed>")); } var exp = new WfExpression { ArgumentToPopulate = arg, ExpressionInActivity = activity, ExpressionString = null }; exp.WfExpressionKnownEntities.Add(new NamedReference() { Name = "E1", ReferencedEntity = resource }); activity.ExpressionMap.Add(exp); // BUG: needed to work around an issue saving relationships }
/// <summary> /// Given a text expression and runtimeArgs, evaluate the expression. /// Expressions must be compatible with DataColumn.Expression. the result is cast to T /// </summary> public object EvaluateExpression(WfExpression expression) { using (Profiler.Measure("RunStateBase.EvaluateExpression")) { ExprType targetType = null; SecurityBypassContext.Elevate(() => targetType = Metadata.GetExpressionType(expression)); var result = expression.EvaluateExpression(this); // force any lists to be resolved to prevent lazy evaluation in a different security context and get rid of the nulls if (targetType.IsList && result != null) { result = ((IEnumerable <IEntity>)result).Where(e => e != null).ToList <IEntity>(); } return(result); } }
static object GetKnownEntities(WfExpression wfExpression, IDictionary <string, Resource> knownEntities) { // Could be a naked references to entities var knownEntitiesCount = knownEntities.Count(); //NOTE: We may want to cache this if (wfExpression.ArgumentToPopulate.Is <ResourceArgument>()) { return(knownEntities.Any() ? knownEntities.First().Value : null); } else if (wfExpression.ArgumentToPopulate.Is <ResourceListArgument>()) { return(knownEntities.Values); } else { return(null); // it's not been populated by anything } }
/// <summary> /// Given a text expression and runtimeArgs, evaluate the expression. /// Expressions must be compatible with DataColumn.Expression. the result is cast to T /// </summary> internal static object EvaluateExpressionImpl(WfExpression wfExpression, IRunState context, IDictionary <string, Resource> knownEntities) { var expression = context.Metadata.CompiledExpressionCache[wfExpression.Id]; if (expression == null) { return(GetKnownEntities(wfExpression, knownEntities)); } else { // Evaluate the expression var eSettings = new EvaluationSettings(); eSettings.TimeZoneName = TimeZoneHelper.SydneyTimeZoneName; // TODO: Workflow engine to provide a timezone to run in. Required for date-time functions. eSettings.ParameterResolver = paramName => context.ResolveParameterValue(paramName, knownEntities); // used for runtime parameters object result = Factory.ExpressionRunner.Run(expression, eSettings).Value; return(CoerceToListIfNeeded(wfExpression, result)); } }
public void KnownAsInExpressionToList() { var entity1 = new Resource(); var expression = new WfExpression { ExpressionString = "E1" }; expression.WfExpressionKnownEntities.Add(new NamedReference { Name = "E1", ReferencedEntity = entity1 }); expression.ArgumentToPopulate = new ResourceListArgument().As <ActivityArgument>(); Workflow wf = new Workflow(); wf.ExpressionMap.Add(expression); var result = ExpressionHelper.EvaluateExpression(expression, TestRunState.CreateDummyState(wf)); Assert.That(result, Is.Not.Null); Assert.That(result, Is.AssignableTo(typeof(IEnumerable <IEntity>))); Assert.That(((IEnumerable <IEntity>)result).Count, Is.EqualTo(1)); }
public static Workflow AddExpressionToArgument(this Workflow wf, WfActivity activity, ActivityArgument destination, string expressionString, bool isTemplate) { if (isTemplate) { expressionString = ExpressionHelper.ConvertTemplateToExpressionString(expressionString); } if (expressionString == null) { throw new ArgumentNullException("expressionString"); } var exp = new WfExpression() { ExpressionString = expressionString, ArgumentToPopulate = destination, ExpressionInActivity = activity, IsTemplateString = false }; activity.ExpressionMap.Add(exp.As <WfExpression>()); return(wf); }
/// <summary> /// Update all the mapping variables associated with the activity. /// </summary> private object EvaluateExpression(IRunState context, WfExpression expression) { try { object evaluatedValue; try { evaluatedValue = context.EvaluateExpression(expression); } catch (System.Data.EvaluateException ex) { throw new WorkflowRunException(ex.Message, ex); } return(evaluatedValue); } catch (WorkflowRunException) { throw; } catch (WorkflowRunException_Internal) { throw; } catch (Exception e) { if (expression != null) { EventLog.Application.WriteError("Exception in Workflow UpdateExpression for Expression id {0} string \"{1}\" : {2}", expression.Id, expression.ExpressionString ?? "null", e.Message); } else { EventLog.Application.WriteError("Exception in Workflow UpdateExpression for Expression (null): {0}", e.Message); } throw; } }
public ExceededArgEvaluationDepthException(long workflowRunId, WfExpression expression) : base("An expression has exceeded the maximum depth for references when being evaluated. This is usually caused by a circular reference: Expression: '{0}'", expression.ExpressionString) { }
/// <summary> /// Get a string representation of the expression resource that is suitable for display. /// </summary> /// <param name="exp">The expression.</param> /// <returns>A string</returns> public static string GetExpressionDisplayString(this WfExpression exp) { return(exp.ExpressionString ?? "<null>"); }
public bool Equals(WfExpression x, WfExpression y) { return(EqualIds(x, y)); }
public int GetHashCode(WfExpression entity) { return(entity.Id.GetHashCode()); }
public ExprType GetExpressionType(WfExpression wfExpr) { var arg = GetArgumentPopulatedByExpression(wfExpr); return(GetExpressionType(arg)); }
private IExpression CompileExpression(WfExpression wfExpr) { var expressionString = wfExpr.ExpressionString; if (string.IsNullOrWhiteSpace(expressionString)) { return(null); // not sure if this is correct, just reproducing old behaviour. } try { var resultType = GetExpressionType(wfExpr); var knownEntities = new Dictionary <string, Resource>(); foreach (var ke in wfExpr.WfExpressionKnownEntities) { if (ke.ReferencedEntity != null && !string.IsNullOrEmpty(ke.Name)) { if (knownEntities.ContainsKey(ke.Name)) { knownEntities[ke.Name] = ke.ReferencedEntity; } else { knownEntities.Add(ke.Name, ke.ReferencedEntity); } } } var bSettings = new BuilderSettings(); bSettings.ScriptHost = ScriptHostType.Evaluate; if (resultType != null && resultType.Type != DataType.None) { bSettings.ExpectedResultType = resultType; } bSettings.StaticParameterResolver = paramName => ResolveParameterType(paramName, knownEntities); // used for runtime parameters return(Factory.ExpressionCompiler.Compile(expressionString, bSettings)); } catch (ParseException ex) { // User/application error with the substance of the calculation expression // e.g. The calculation «[Hello» has a problem: Mal-formed string literal - cannot find termination symbol. (pos 1) var activityName = (wfExpr?.ExpressionInActivity?.Name) ?? "Unknown"; string message = $"The calculation «{expressionString}» in «{activityName}» has a problem: {ex.Message}"; EventLog.Application.WriteWarning(message); AddValidationError(message); } catch (Exception ex) { // Internal error var activityName = (wfExpr?.ExpressionInActivity?.Name) ?? "Unknown"; string message = $"Internal error while compiling expression «{expressionString}» in «{activityName}»"; EventLog.Application.WriteError(ex.ToString( )); AddValidationError(message); } return(null); }