예제 #1
0
        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));
        }
예제 #2
0
        /// <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);
            }
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
        }
예제 #5
0
        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);
            }
        }
예제 #6
0
        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);
        }
예제 #7
0
        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);
        }
예제 #8
0
        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
        }
예제 #9
0
        /// <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);
            }
        }
예제 #10
0
        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
            }
        }
예제 #11
0
        /// <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));
            }
        }
예제 #12
0
        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));
        }
예제 #13
0
        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);
        }
예제 #14
0
        /// <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;
            }
        }
예제 #15
0
 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)
 {
 }
예제 #16
0
 /// <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>");
 }
예제 #17
0
 public bool Equals(WfExpression x, WfExpression y)
 {
     return(EqualIds(x, y));
 }
예제 #18
0
 public int GetHashCode(WfExpression entity)
 {
     return(entity.Id.GetHashCode());
 }
예제 #19
0
        public ExprType GetExpressionType(WfExpression wfExpr)
        {
            var arg = GetArgumentPopulatedByExpression(wfExpr);

            return(GetExpressionType(arg));
        }
예제 #20
0
        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);
        }