/// <summary>
        /// Validates the inputs. Returns true if all of the UI controls contain valid values. Otherwise, returns false.
        /// </summary>
        /// <returns>true if all of the UI controls contain valid values. Otherwise, false.</returns>
        public override bool ValidateInputs()
        {
            Logger.Instance.WriteMethodEntry();

            try
            {
                // Use the controller to validate standard activity controls
                // and return false if a problem was identified
                if (!this.controller.ValidateInputs())
                {
                    return false;
                }

                try
                {
                    // Verify that the target lookup is valid
                    LookupEvaluator targetLookup = new LookupEvaluator(this.publicationTarget.Value);
                    if (!targetLookup.IsValidTarget)
                    {
                        this.controller.ValidationError = string.Format(CultureInfo.CurrentUICulture, ActivitySettings.TargetLookupValidationError, this.publicationTarget.Value);
                        return false;
                    }
                }
                catch (WorkflowActivityLibraryException ex)
                {
                    // If an exception was thrown while attempting to parse lookups, report the error
                    this.controller.ValidationError = ex.Message;
                    return false;
                }

                // Verify that the supplied conflict filter is a valid XPath query
                if (!ExpressionEvaluator.IsXPath(this.conflictFilter.Value))
                {
                    this.controller.ValidationError = ActivitySettings.ConflictFilterValidationError;
                    return false;
                }

                // Verify that the supplied conflict filter contains the [//Value] lookup
                if (!this.conflictFilter.Value.ToUpperInvariant().Contains("[//VALUE]"))
                {
                    this.controller.ValidationError = ActivitySettings.ConflictFilterValueLookupValidationError;
                    return false;
                }

                ExpressionEvaluator evaluator = new ExpressionEvaluator();

                if (this.queryLdap.Value)
                {
                    // Loop through all active query listings and make sure they are valid
                    foreach (DefinitionListing query in this.ldapQueries.DefinitionListings.Where(query => query.Active))
                    {
                        // If a value is missing for key or query, the definition
                        // will be null and the listing fails validation
                        if (query.Definition == null)
                        {
                            this.controller.ValidationError = ActivitySettings.LdapQueryDefinitionValidationError;
                            return false;
                        }

                        // Make sure that the specified XPath filter is properly formatted
                        if (!query.Definition.Right.ToUpperInvariant().Contains("[//VALUE]"))
                        {
                            this.controller.ValidationError = ActivitySettings.LdapQueryDefinitionValueLookupValidationError;
                            return false;
                        }

                        // If it's an expression, make sure it's properly formatted
                        if (ExpressionEvaluator.IsExpression(query.Definition.Left))
                        {
                            try
                            {
                                evaluator.ParseExpression(query.Definition.Left);
                            }
                            catch (WorkflowActivityLibraryException ex)
                            {
                                this.controller.ValidationError = ex.Message;
                                return false;
                            }
                        }
                    }
                }

                // Count the active value expressions
                int count = this.valueExpressions.DefinitionListings.Count(valueExpression => valueExpression.Active);

                // Loop through all active update listings and make sure they are valid
                int i = 1;
                foreach (DefinitionListing valueExpression in this.valueExpressions.DefinitionListings.Where(valueExpression => valueExpression.Active))
                {
                    if (string.IsNullOrEmpty(valueExpression.State.Left))
                    {
                        // If a value is missing for the expression, fail validation
                        this.controller.ValidationError = ActivitySettings.ValueExpressionValidationError;
                        return false;
                    }

                    // Attempt to parse the value expression
                    try
                    {
                        evaluator.ParseExpression(valueExpression.State.Left);
                    }
                    catch (WorkflowActivityLibraryException ex)
                    {
                        this.controller.ValidationError = ex.Message;
                        return false;
                    }

                    // Verify that the [//UniquenessKey] lookup is only present in the last value expression
                    bool containsKey = valueExpression.State.Left.ToUpperInvariant().Contains("[//UNIQUENESSKEY]");
                    if (i < count && containsKey)
                    {
                        this.controller.ValidationError = ActivitySettings.ValueExpressionUniquenessKeyValidationError;
                        return false;
                    }

                    if (i == count && !containsKey)
                    {
                        this.controller.ValidationError = ActivitySettings.ValueExpressionMissingUniquenessKeyValidationError;
                        return false;
                    }

                    i += 1;
                }

                try
                {
                    if (!string.IsNullOrEmpty(this.activityExecutionCondition.Value))
                    {
                        evaluator.ParseExpression(this.activityExecutionCondition.Value);

                        // Verify that the activity execution condition resolves to a Boolean value
                        if (!evaluator.IsBooleanExpression(this.activityExecutionCondition.Value))
                        {
                            this.controller.ValidationError = ActivitySettings.ActivityExecutionConditionValidationError;
                            return false;
                        }
                    }
                }
                catch (WorkflowActivityLibraryException ex)
                {
                    this.controller.ValidationError = ex.Message;
                    return false;
                }

                // If no errors were found, clear any validation error and return true
                this.controller.ValidationError = string.Empty;
                return true;
            }
            catch (Exception e)
            {
                Logger.Instance.ReportError(e);
                throw;
            }
            finally
            {
                Logger.Instance.WriteMethodExit();
            }
        }
Beispiel #2
0
        /// <summary>
        /// Handles the ExecuteCode event of the Prepare CodeActivity.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
        private void Prepare_ExecuteCode(object sender, EventArgs e)
        {
            Logger.Instance.WriteMethodEntry(EventIdentifier.ResolveLookupsPrepareExecuteCode);

            try
            {
                // Get the parent workflow so we can use it to pull request target, requestor, etc.
                // and access the workflow dictionary
                SequentialWorkflow parentWorkflow;
                if (!SequentialWorkflow.TryGetContainingWorkflow(this, out parentWorkflow))
                {
                    throw Logger.Instance.ReportError(EventIdentifier.ResolveLookupsPrepareExecuteCodeUnableToGetParentWorkflowError, new InvalidOperationException(Messages.UnableToGetParentWorkflowError));
                }

                // Determine if the parent workflow is an authorization workflow running against a create request
                // In this situation, lookups against target will fail because the resource does not yet exist.
                // We will need to convert [//Target/...] to [//Delta/...] to facilitate resolution
                // This behavior replicates FIM's native behavior
                if (this.Request.CurrentRequest.Operation == OperationType.Create &&
                    this.Request.CurrentRequest.AuthorizationWorkflowInstances != null)
                {
                    foreach (UniqueIdentifier ui in this.Request.CurrentRequest.AuthorizationWorkflowInstances)
                    {
                        Guid authZWorkflowInstanceId;
                        if (ui.TryGetGuid(out authZWorkflowInstanceId) && authZWorkflowInstanceId == this.WorkflowInstanceId)
                        {
                            this.authZCreate = true;
                            break;
                        }
                    }
                }

                foreach (string s in this.Lookups.Keys)
                {
                    Logger.Instance.WriteVerbose(EventIdentifier.ResolveLookupsPrepareExecuteCode, "Constructing lookup evaluator for lookup: '{0}'.", s);

                    LookupEvaluator originalLookup = new LookupEvaluator(s);
                    LookupEvaluator resolvableLookup = new LookupEvaluator(s);

                    // There are certain conditions which will prompt the activity to dynamically replace the original lookup with an alternate
                    // This is done at the paramater, swapping [//Effective/...] with [//Delta/...] or [//Target/...], for example
                    if (this.authZCreate && (originalLookup.Parameter == LookupParameter.Target || originalLookup.Parameter == LookupParameter.Effective))
                    {
                        Logger.Instance.WriteVerbose(EventIdentifier.ResolveLookupsPrepareExecuteCode, "The lookup '{0}' will be processed as [//Delta] lookup.", s);

                        // When resolving a [//Target/...] or [//Effective/...] lookup during authZ on a create request,
                        // we will always pull the value from the request delta because the target resource will not yet be available for read
                        string lookupString = s.Replace(string.Format(CultureInfo.InvariantCulture, "[//{0}/", s.Substring(3, originalLookup.Parameter.ToString().Length)), string.Format(CultureInfo.InvariantCulture, "[//{0}/", LookupParameter.Delta));
                        resolvableLookup = new LookupEvaluator(lookupString);
                    }
                    else if (originalLookup.Parameter == LookupParameter.Effective)
                    {
                        // When resolving a [//Effective/...] lookup, we need to determine if we should resolve against the request delta or the target resource
                        // If the attribute tied to the Effective parameter exists in the request parameters, the associated value will always supersede the
                        // attribute value on the target resource
                        bool useDelta = false;
                        if (originalLookup.Components.Count > 1)
                        {
                            ReadOnlyCollection<CreateRequestParameter> requestParameters = this.Request.CurrentRequest.ParseParameters<CreateRequestParameter>();
                            var query = from p in requestParameters where p.PropertyName.Equals(originalLookup.Components[1], StringComparison.OrdinalIgnoreCase) select p.PropertyName;
                            if (query.Any())
                            {
                                useDelta = true;

                                Logger.Instance.WriteVerbose(EventIdentifier.ResolveLookupsPrepareExecuteCode, "The [//Effective] lookup '{0}' will be processed as [//Delta] lookup.", s);
                            }
                        }

                        // Either replace [//Effective/...] with [//Delta/...] or [//Target/...] to facilitate resolution
                        // The activity will look for this lookup when resolving the original effective lookup
                        string lookupString = s.Replace(string.Format(CultureInfo.InvariantCulture, "[//{0}/", s.Substring(3, originalLookup.Parameter.ToString().Length)), useDelta ? string.Format(CultureInfo.InvariantCulture, "[//{0}/", LookupParameter.Delta) : string.Format(CultureInfo.InvariantCulture, "[//{0}/", LookupParameter.Target));

                        resolvableLookup = new LookupEvaluator(lookupString);
                    }

                    // Add the lookup parameter to the list of parameters for the activity so we know which resolutions are required
                    // Also establish a new entry in the values dictionary
                    // If the resolvable lookup is different than the original, add the pair to the alternates dictionary so we can find it later during resolution
                    if (!this.parameters.Contains(resolvableLookup.Parameter))
                    {
                        this.parameters.Add(resolvableLookup.Parameter);
                    }

                    if (!this.values.ContainsKey(resolvableLookup.Lookup))
                    {
                        this.values.Add(resolvableLookup.Lookup, new List<object>());
                    }

                    if (originalLookup.Lookup != resolvableLookup.Lookup)
                    {
                        this.alternateLookups.Add(originalLookup.Lookup, resolvableLookup.Lookup);
                    }

                    // Add each of the lookup's reads to the read dictionary which represents all lookups
                    // This single dictionary will ensure the minimum number of reads are performed
                    foreach (string key in resolvableLookup.Reads.Keys)
                    {
                        if (!this.reads.ContainsKey(key))
                        {
                            this.reads.Add(key, new List<string> { resolvableLookup.Reads[key] });
                        }
                        else if (!this.reads[key].Contains(resolvableLookup.Reads[key]))
                        {
                            this.reads[key].Add(resolvableLookup.Reads[key]);
                        }
                    }

                    // If resolving a [//WorkflowData/...] lookup, publish the value of the specified
                    // workflow dictionary key to the resources or values dictionary
                    // Make sure we only do this once for each workflow dictionary key because the publish method will stack values if it's executed more than once
                    if (resolvableLookup.Parameter == LookupParameter.WorkflowData &&
                        resolvableLookup.Components.Count > 1 &&
                        parentWorkflow.WorkflowDictionary.ContainsKey(resolvableLookup.Components[1]) &&
                        parentWorkflow.WorkflowDictionary[resolvableLookup.Components[1]] != null &&
                        !this.values.ContainsKey(string.Format(CultureInfo.InvariantCulture, "{0}/{1}", LookupParameter.WorkflowData, resolvableLookup.Components[1])))
                    {
                        this.Publish(string.Format(CultureInfo.InvariantCulture, "{0}/{1}", LookupParameter.WorkflowData, resolvableLookup.Components[1]), parentWorkflow.WorkflowDictionary[resolvableLookup.Components[1]]);
                    }
                }

                // If we have reads, add the keys of the read dictionary to the for each loop which will perform them
                if (this.reads.Count > 0)
                {
                    this.ForEachRead.InitialChildData = this.reads.Keys.ToArray();
                }

                // Publish Request, Requestor, and Target to the resource and values dictionary
                // regardless of lookup parameter
                this.Publish(LookupParameter.Request.ToString(), parentWorkflow.RequestId);
                this.Publish(LookupParameter.Requestor.ToString(), parentWorkflow.ActorId);
                if (this.parameters.Contains(LookupParameter.Target))
                {
                    this.Publish(LookupParameter.Target.ToString(), parentWorkflow.TargetId);
                }

                // Publish the supplied value for [//Value]
                if (this.parameters.Contains(LookupParameter.Value))
                {
                    this.Publish(LookupParameter.Value.ToString(), this.Value);
                }

                // Resolve lookups associated with the [//Delta/...] parameter
                if (this.parameters.Contains(LookupParameter.Delta))
                {
                    if (this.Request.CurrentRequest.Operation != OperationType.SystemEvent)
                    {
                        this.PublishRequestDelta(this.Request.CurrentRequest, false);
                    }
                }

                // If necessary, add the supplied query results to the resource dictionary
                // to support resolution of [//Queries/...] lookups
                if (this.parameters.Contains(LookupParameter.Queries))
                {
                    if (this.QueryResults != null)
                    {
                        foreach (string query in this.QueryResults.Keys)
                        {
                            this.Publish(string.Format(CultureInfo.InvariantCulture, "{0}/{1}", LookupParameter.Queries, query), this.QueryResults[query]);
                        }
                    }
                    else
                    {
                        Logger.Instance.WriteVerbose(EventIdentifier.ResolveLookupsPrepareExecuteCode, "The [//Queries/...] lookup(s) don't have any supplied results.");
                    }
                }

                // Preemptively build the XPath filter which identifies approvals associated with the request
                // The activity will only be used if we are resolving a [//Approvers/...] lookup
                this.FindApprovals.XPathFilter = string.Format(CultureInfo.InvariantCulture, "/Approval[Request = '{0}']", parentWorkflow.RequestId);
            }
            finally
            {
                Logger.Instance.WriteMethodExit(EventIdentifier.ResolveLookupsPrepareExecuteCode);
            }
        }
        /// <summary>
        /// Validates the inputs. Returns true if all of the UI controls contain valid values. Otherwise, returns false.
        /// </summary>
        /// <returns>true if all of the UI controls contain valid values. Otherwise, false.</returns>
        public override bool ValidateInputs()
        {
            Logger.Instance.WriteMethodEntry();

            try
            {
                // Use the controller to validate standard activity controls
                // and return false if a problem was identified
                if (!this.controller.ValidateInputs())
                {
                    return false;
                }

                ExpressionEvaluator evaluator = new ExpressionEvaluator();

                // Verify that the resource type is valid
                if (!Regex.Match(this.resourceType.Value, RegexPattern).Success)
                {
                    this.controller.ValidationError = string.Format(CultureInfo.CurrentUICulture, ActivitySettings.ResourceTypeValidationError, this.resourceType.Value);
                    return false;
                }

                if (this.advanced.Value)
                {
                    if (this.queryResources.Value)
                    {
                        // Loop through all active query listings and make sure they are valid
                        foreach (DefinitionListing query in this.queries.DefinitionListings)
                        {
                            if (query.Active)
                            {
                                // If a value is missing for key or query, the definition
                                // will be null and the listing fails validation
                                if (query.Definition == null)
                                {
                                    this.controller.ValidationError = ActivitySettings.QueryDefinitionValidationError;
                                    return false;
                                }

                                // Make sure that the specified query key is properly formatted
                                if (!Regex.Match(query.Definition.Left, RegexPattern).Success)
                                {
                                    this.controller.ValidationError = string.Format(CultureInfo.CurrentUICulture, ActivitySettings.QueryDefintionLeftValidationError, query.Definition.Left);
                                    return false;
                                }

                                // Make sure that the specified XPath filter is properly formatted
                                if (!ExpressionEvaluator.IsXPath(query.Definition.Right))
                                {
                                    this.controller.ValidationError = string.Format(CultureInfo.CurrentUICulture, ActivitySettings.QueryDefintionRightValidationError, query.Definition.Left);
                                    return false;
                                }
                            }
                        }
                    }

                    try
                    {
                        if (!string.IsNullOrEmpty(this.activityExecutionCondition.Value))
                        {
                            evaluator.ParseExpression(this.activityExecutionCondition.Value);

                            // Verify that the activity execution condition resolves to a Boolean value
                            if (!evaluator.IsBooleanExpression(this.activityExecutionCondition.Value))
                            {
                                this.controller.ValidationError = ActivitySettings.ActivityExecutionConditionValidationError;
                                return false;
                            }
                        }

                        if (!string.IsNullOrEmpty(this.iteration.Value))
                        {
                            evaluator.ParseExpression(this.iteration.Value);
                        }

                        if (GetActorType(this.actorType.Value) == ActorType.Resolve)
                        {
                            evaluator.ParseExpression(this.actorString.Value);
                        }

                        // Verify that the supplied conflict filter is a valid XPath query, if necessary
                        if (this.checkForConflict.Value && !ExpressionEvaluator.IsXPath(this.conflictFilter.Value))
                        {
                            this.controller.ValidationError = ActivitySettings.ConflictResourceSearchFilterValidationError;
                            return false;
                        }

                        // If necessary, parse the created resource ID target lookup to ensure its validity
                        if (!string.IsNullOrEmpty(this.createdResourceIdTarget.Value))
                        {
                            LookupEvaluator createdTargetLookup = new LookupEvaluator(this.createdResourceIdTarget.Value);
                            if (!createdTargetLookup.IsValidTarget)
                            {
                                this.controller.ValidationError = string.Format(CultureInfo.CurrentUICulture, ActivitySettings.TargetLookupValidationError, this.createdResourceIdTarget.Value);
                                return false;
                            }
                        }

                        // If necessary, parse the conflicting resource ID target lookup to ensure its validity
                        if (this.checkForConflict.Value && !string.IsNullOrEmpty(this.conflictingResourceIdTarget.Value))
                        {
                            LookupEvaluator conflictingTargetLookup = new LookupEvaluator(this.conflictingResourceIdTarget.Value);
                            if (!conflictingTargetLookup.IsValidTarget)
                            {
                                this.controller.ValidationError = string.Format(CultureInfo.CurrentUICulture, ActivitySettings.TargetLookupValidationError, this.conflictingResourceIdTarget.Value);
                                return false;
                            }
                        }
                    }
                    catch (WorkflowActivityLibraryException ex)
                    {
                        // If an exception was thrown while attempting to parse lookups, report the error
                        this.controller.ValidationError = ex.Message;
                        return false;
                    }

                    if (this.applyAuthorizationPolicy.Value && GetActorType(this.actorType.Value) == ActorType.Service)
                    {
                        this.controller.ValidationError = ActivitySettings.RequestActorValidationError;
                        return false;
                    }
                }

                // Loop through all active definition listings and make sure they are valid
                foreach (DefinitionListing listing in this.attributes.DefinitionListings)
                {
                    if (!listing.Active)
                    {
                        continue;
                    }

                    if (listing.Definition == null)
                    {
                        // If a value is missing for source or target, the definition
                        // will be null and the listing fails validation
                        // Because the activity allows for the creation of a resource without any attributes,
                        // we need to check if this is the only active listing for the form and, if so, 
                        // verify that all fields have been left blank before failing validation
                        int countActive = this.attributes.DefinitionListings.Count(l => l.Active);
                        if (countActive != 1 ||
                            !string.IsNullOrEmpty(listing.State.Left) ||
                            !string.IsNullOrEmpty(listing.State.Right))
                        {
                            this.controller.ValidationError = ActivitySettings.AttributeDefinitionValidationError;
                            return false;
                        }
                    }
                    else
                    {
                        // Attempt to parse the source expression and fail validation
                        // if an exception is thrown by the expression evaluator
                        try
                        {
                            evaluator.ParseExpression(listing.Definition.Left);
                        }
                        catch (WorkflowActivityLibraryException ex)
                        {
                            this.controller.ValidationError = ex.Message;
                            return false;
                        }

                        ParameterType targetType = ParameterType.String;
                        try
                        {
                            targetType = ExpressionEvaluator.DetermineParameterType(listing.Definition.Right, true);
                        }
                        catch (WorkflowActivityLibraryException)
                        {
                        }

                        // Target variables are valid
                        // For anything else, make sure that the target attribute matches the regular
                        // expression for FIM attributes
                        if (targetType != ParameterType.Variable)
                        {
                            if (!Regex.Match(listing.Definition.Right, RegexPattern).Success)
                            {
                                this.controller.ValidationError = string.Format(CultureInfo.CurrentUICulture, ActivitySettings.TargetValidationError, listing.Definition.Right);
                                return false;
                            }
                        }
                    }
                }

                // Verify that no [//Query/...] or [//Value/...] expressions exist
                // if the query resources or iteration options are not enabled, respectively
                bool containsQueryExpressions = false;
                bool containsValueExpressions = false;

                foreach (LookupEvaluator lookup in evaluator.LookupCache.Keys.Select(key => new LookupEvaluator(key)))
                {
                    if (lookup.Parameter == LookupParameter.Queries)
                    {
                        containsQueryExpressions = true;
                    }

                    if (lookup.Parameter == LookupParameter.Value)
                    {
                        containsValueExpressions = true;
                    }
                }

                if (!this.queryResources.Value && containsQueryExpressions)
                {
                    this.controller.ValidationError = ActivitySettings.QueryResourcesValidationError;
                    return false;
                }

                if (string.IsNullOrEmpty(this.iteration.Value) && containsValueExpressions)
                {
                    this.controller.ValidationError = ActivitySettings.IterationValidationError;
                    return false;
                }

                // If no errors were found, clear any validation error and return true
                this.controller.ValidationError = string.Empty;
                return true;
            }
            catch (Exception e)
            {
                Logger.Instance.ReportError(e);
                throw;
            }
            finally
            {
                Logger.Instance.WriteMethodExit();
            }
        }
        /// <summary>
        /// Validates the inputs. Returns true if all of the UI controls contain valid values. Otherwise, returns false.
        /// </summary>
        /// <returns>true if all of the UI controls contain valid values. Otherwise, false.</returns>
        public override bool ValidateInputs()
        {
            Logger.Instance.WriteMethodEntry();

            try
            {
                // Use the controller to validate standard activity controls
                // and return false if a problem was identified
                if (!this.controller.ValidateInputs())
                {
                    return false;
                }

                ExpressionEvaluator evaluator = new ExpressionEvaluator();

                if (this.advanced.Value)
                {
                    if (this.queryResources.Value)
                    {
                        // Loop through all active query listings and make sure they are valid
                        foreach (DefinitionListing query in this.queries.DefinitionListings.Where(query => query.Active))
                        {
                            // If a value is missing for key or query, the definition
                            // will be null and the listing fails validation
                            if (query.Definition == null)
                            {
                                this.controller.ValidationError = ActivitySettings.QueryDefinitionValidationError;
                                return false;
                            }

                            // Make sure that the specified query key is properly formatted
                            if (!Regex.Match(query.Definition.Left, RegexPattern).Success)
                            {
                                this.controller.ValidationError = string.Format(CultureInfo.CurrentUICulture, ActivitySettings.QueryDefintionLeftValidationError, query.Definition.Left);
                                return false;
                            }

                            // Make sure that the specified XPath filter is properly formatted
                            if (!ExpressionEvaluator.IsXPath(query.Definition.Right))
                            {
                                this.controller.ValidationError = string.Format(CultureInfo.CurrentUICulture, ActivitySettings.QueryDefintionRightValidationError, query.Definition.Left);
                                return false;
                            }
                        }
                    }

                    try
                    {
                        if (!string.IsNullOrEmpty(this.activityExecutionCondition.Value))
                        {
                            evaluator.ParseExpression(this.activityExecutionCondition.Value);

                            // Verify that the activity execution condition resolves to a Boolean value
                            if (!evaluator.IsBooleanExpression(this.activityExecutionCondition.Value))
                            {
                                this.controller.ValidationError = ActivitySettings.ActivityExecutionConditionValidationError;
                                return false;
                            }
                        }

                        if (!string.IsNullOrEmpty(this.iteration.Value))
                        {
                            evaluator.ParseExpression(this.iteration.Value);
                        }

                        if (GetActorType(this.actorType.Value) == ActorType.Resolve)
                        {
                            evaluator.ParseExpression(this.actorString.Value);
                        }
                    }
                    catch (WorkflowActivityLibraryException ex)
                    {
                        this.controller.ValidationError = ex.Message;
                        return false;
                    }

                    if (this.applyAuthorizationPolicy.Value && GetActorType(this.actorType.Value) == ActorType.Service)
                    {
                        this.controller.ValidationError = ActivitySettings.RequestActorValidationError;
                        return false;
                    }
                }

                // Loop through all active update listings and make sure they are valid
                foreach (DefinitionListing update in this.updates.DefinitionListings.Where(update => update.Active))
                {
                    if (update.Definition == null)
                    {
                        // If a value is missing for source or target, the definition
                        // will be null and the listing fails validation
                        this.controller.ValidationError = ActivitySettings.UpdateDefinitionValidationError;
                        return false;
                    }

                    // Attempt to parse the source expression and target lookup or variable
                    // Fail validation if an exception is thrown for either
                    try
                    {
                        evaluator.ParseExpression(update.Definition.Left);
                        ParameterType targetType = ParameterType.Lookup;
                        try
                        {
                            targetType = ExpressionEvaluator.DetermineParameterType(update.Definition.Right);
                        }
                        catch (WorkflowActivityLibraryException)
                        {
                        }

                        // Target variables are valid
                        // Target lookups require further evaluation to determine if they represent a valid target
                        if (targetType != ParameterType.Variable)
                        {
                            LookupEvaluator lookup = new LookupEvaluator(update.Definition.Right);
                            if (!lookup.IsValidTarget)
                            {
                                this.controller.ValidationError = string.Format(CultureInfo.CurrentUICulture, ActivitySettings.TargetLookupValidationError, update.Definition.Right);
                                return false;
                            }
                        }
                    }
                    catch (WorkflowActivityLibraryException ex)
                    {
                        this.controller.ValidationError = ex.Message;
                        return false;
                    }
                }

                // Verify that no [//Query/...] or [//Value/...] expressions exist
                // if the query resources or iteration options are not enabled, respectively
                bool containsQueryExpressions = false;
                bool containsValueExpressions = false;

                foreach (LookupEvaluator lookup in evaluator.LookupCache.Keys.Select(key => new LookupEvaluator(key)))
                {
                    if (lookup.Parameter == LookupParameter.Queries)
                    {
                        containsQueryExpressions = true;
                    }

                    if (lookup.Parameter == LookupParameter.Value)
                    {
                        containsValueExpressions = true;
                    }
                }

                if (!this.queryResources.Value && containsQueryExpressions)
                {
                    this.controller.ValidationError = ActivitySettings.QueryResourcesValidationError;
                    return false;
                }

                if (string.IsNullOrEmpty(this.iteration.Value) && containsValueExpressions)
                {
                    this.controller.ValidationError = ActivitySettings.IterationValidationError;
                    return false;
                }

                // If no errors were found, clear any validation error and return true
                this.controller.ValidationError = string.Empty;
                return true;
            }
            catch (Exception e)
            {
                Logger.Instance.ReportError(e);
                throw;
            }
            finally
            {
                Logger.Instance.WriteMethodExit();
            }
        }
Beispiel #5
0
        /// <summary>
        /// Handles the ExecuteCode event of the BuildRequests CodeActivity.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        private void BuildRequests_ExecuteCode(object sender, EventArgs e)
        {
            Logger.Instance.WriteMethodEntry(EventIdentifier.UpdateLookupsBuildRequestsExecuteCode);

            try
            {
                foreach (UpdateLookupDefinition update in this.UpdateLookupDefinitions)
                {
                    LookupEvaluator lookup = new LookupEvaluator(update.TargetLookup);
                    if (lookup.TargetIsWorkflowDictionary)
                    {
                        // Get the parent workflow to facilitate access to the workflow dictionary
                        SequentialWorkflow parentWorkflow;
                        SequentialWorkflow.TryGetContainingWorkflow(this, out parentWorkflow);

                        // For simplicity, start by adding the key to the parent workflow dictionary so we can assume that its there
                        if (!parentWorkflow.WorkflowDictionary.ContainsKey(lookup.TargetAttribute))
                        {
                            parentWorkflow.WorkflowDictionary.Add(lookup.TargetAttribute, null);
                        }

                        if (update.Mode == UpdateMode.Modify)
                        {
                            parentWorkflow.WorkflowDictionary[lookup.TargetAttribute] = update.Value;
                        }
                        else if (update.Value != null)
                        {
                            // Use reflection to determine the expected List<> type based on the value
                            // Also get the Add and Remove methods for the list
                            Type listType = typeof(List<>).MakeGenericType(new Type[] { update.Value.GetType() });
                            MethodInfo add = listType.GetMethod("Add");
                            MethodInfo remove = listType.GetMethod("Remove");

                            switch (update.Mode)
                            {
                                case UpdateMode.Insert:
                                    if (parentWorkflow.WorkflowDictionary[lookup.TargetAttribute] == null)
                                    {
                                        parentWorkflow.WorkflowDictionary[lookup.TargetAttribute] = update.Value;
                                    }
                                    else if (parentWorkflow.WorkflowDictionary[lookup.TargetAttribute].GetType() == update.Value.GetType())
                                    {
                                        // Single value, create a new instance of the appropriate List<> type
                                        // and add both values: existing and new
                                        object existingValue = parentWorkflow.WorkflowDictionary[lookup.TargetAttribute];
                                        parentWorkflow.WorkflowDictionary[lookup.TargetAttribute] = Activator.CreateInstance(listType);
                                        add.Invoke(parentWorkflow.WorkflowDictionary[lookup.TargetAttribute], new object[] { existingValue });
                                        add.Invoke(parentWorkflow.WorkflowDictionary[lookup.TargetAttribute], new object[] { update.Value });
                                    }
                                    else if (parentWorkflow.WorkflowDictionary[lookup.TargetAttribute].GetType() == listType)
                                    {
                                        // The dictionary key is a list of the expected type, add the value
                                        add.Invoke(parentWorkflow.WorkflowDictionary[lookup.TargetAttribute], new object[] { update.Value });
                                    }
                                    else
                                    {
                                        // We have a problem and need to report an error
                                        throw Logger.Instance.ReportError(new WorkflowActivityLibraryException(Messages.UpdateLookup_InsertVariableError, update.Value.GetType(), lookup.TargetAttribute, parentWorkflow.WorkflowDictionary[lookup.TargetAttribute].GetType()));
                                    }

                                    break;
                                case UpdateMode.Remove:
                                    if (parentWorkflow.WorkflowDictionary[lookup.TargetAttribute] == null)
                                    {
                                        // Do nothing
                                    }
                                    else if (parentWorkflow.WorkflowDictionary[lookup.TargetAttribute].Equals(update.Value))
                                    {
                                        // A single matching value exists, clear the variable
                                        parentWorkflow.WorkflowDictionary[lookup.TargetAttribute] = null;
                                    }
                                    else if (parentWorkflow.WorkflowDictionary[lookup.TargetAttribute].GetType() == listType)
                                    {
                                        // The variable is a list of the expected type, attempt to remove the value
                                        remove.Invoke(parentWorkflow.WorkflowDictionary[lookup.TargetAttribute], new object[] { update.Value });

                                        // Check the count on the list to determine if we are down to a single value or have no value
                                        // If so, adjust the value of the variable accordingly to eliminate the list
                                        object listValue = null;
                                        int i = 0;
                                        foreach (object o in (IEnumerable)parentWorkflow.WorkflowDictionary[lookup.TargetAttribute])
                                        {
                                            i += 1;
                                            listValue = o;
                                        }

                                        if (i <= 1)
                                        {
                                            parentWorkflow.WorkflowDictionary[lookup.TargetAttribute] = listValue;
                                        }
                                    }

                                    break;
                            }
                        }

                        // If we ended up with a null value in the workflow dictionary key,
                        // remove the key to cleanup after ourselves
                        if (parentWorkflow.WorkflowDictionary[lookup.TargetAttribute] == null)
                        {
                            parentWorkflow.WorkflowDictionary.Remove(lookup.TargetAttribute);
                        }
                    }
                    else if (!string.IsNullOrEmpty(lookup.TargetResourceLookup) &&
                             this.TargetLookups.ContainsKey(lookup.TargetResourceLookup) &&
                             this.TargetLookups[lookup.TargetResourceLookup] != null)
                    {
                        // Based on the type of the resolved target lookup (should be Guid or List<Guid>)
                        // build the list of target resources for the update
                        List<Guid> targets = new List<Guid>();
                        if (this.TargetLookups[lookup.TargetResourceLookup] is Guid)
                        {
                            targets.Add((Guid)this.TargetLookups[lookup.TargetResourceLookup]);
                        }
                        else if (this.TargetLookups[lookup.TargetResourceLookup].GetType() == typeof(List<Guid>))
                        {
                            targets.AddRange((List<Guid>)this.TargetLookups[lookup.TargetResourceLookup]);
                        }

                        foreach (Guid target in targets)
                        {
                            // Add the target to the update requests dictionary, if it doesn't already exist,
                            // and add the new update request parameter
                            if (!this.PendingRequests.ContainsKey(target))
                            {
                                this.PendingRequests.Add(target, new List<UpdateRequestParameter>());
                            }

                            this.PendingRequests[target].Add(new UpdateRequestParameter(lookup.TargetAttribute, update.Mode, update.Value));
                        }
                    }
                }

                // If there are requests that need to be submitted to fulfill the updates,
                // assign the list of targets to the for each loop which will evaluate each change
                // to determine if they will result in changes
                if (this.PendingRequests.Count > 0)
                {
                    this.ForEachPending.InitialChildData = this.PendingRequests.Keys.ToList();
                }

                Logger.Instance.WriteVerbose(EventIdentifier.UpdateLookupsBuildRequestsExecuteCode, "The number of requests that need to be submitted to fulfill the updates: '{0}'.", this.PendingRequests.Count);
            }
            finally
            {
                Logger.Instance.WriteMethodExit(EventIdentifier.UpdateLookupsBuildRequestsExecuteCode);
            }
        }