Workflow Foundation 4 - Результаты ActivityFunc <bool>всегда ложны, хотя метод выполнения показывает true - PullRequest
1 голос
/ 18 октября 2010

Ello, у меня проблема с пользовательским действием, которое предварительно оценивает `ActivityFunc` и возвращает false, даже если оно выполнено в Execute, чтобы быть истинным. Вот моя деятельность


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using System.ComponentModel;
using System.Activities.Presentation;

namespace SomeActivities
{
    /// 
    /// Root of any number of activities used to check for a specific set of conditions to be true (Trigger Conditions) 
    /// 
    public sealed class Trigger : NativeActivity, IActivityTemplateFactory
    {
        /// 
        /// The initial Condition that determines if the trigger should be scheduled
        /// 
        /// The condition.
        [RequiredArgument]
        public ActivityFunc <bool> Condition { get; set; }

        /// 
        /// The resulting action that is scheduled if the Condition is true
        /// 
        /// The child.
        [RequiredArgument]
        public ActivityAction Child { get; set; }

        /// 
        /// Gets or sets the value holding whether or not the trigger matches the condition
        /// 
        /// The type of the match.
        public MatchType MatchType{ get; set; }

        private CompletionCallback<bool> OnExecutionCompleteCallBack;

        protected override void Execute(NativeActivityContext context)
        {
            this.OnExecutionCompleteCallBack = this.OnConditionComplete;
            context.ScheduleFunc<bool>(this.Condition, this.OnExecutionCompleteCallBack);
        }

        public void OnConditionComplete(NativeActivityContext context, ActivityInstance instance, bool result)
        {
            if (instance.State == ActivityInstanceState.Canceled)
            {
                context.Abort();
                return;
            }

            //check if Condition evaluation returns true
            //Always show as false
            if (result)
            {
                //If so then schedule child Activity
                context.ScheduleAction(Child);
            }
        }

        Activity IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
        {
            return new Trigger()
            {
                Child = new ActivityAction()
                {
                    DisplayName = "Trigger Child Action"
                },

                Condition = new ActivityFunc<bool>()
                {
                    DisplayName = "Trigger Conditionals",
                    Result = new DelegateOutArgument<bool>()
                },
                DisplayName = "Trigger",
                MatchType = MatchType.Matches,

            };
        }
    }
}

Поэтому, когда мое условие оценивается в методе Execute, оно вызывает OnConditionComplete с результатом (что всегда ложно), даже если я печатаю результат условия как true. Так что здесь явно не так, что я не вижу?

Обновление

Хорошо, я думаю, что Марис говорит о наличии обратного вызова в классе и просто о том, что метод OnConditionComplete указывает на обратный вызов. Я изменил это, но не видел изменений. Если бы я мог каким-то образом извлечь значение из условия ActivityFunc<bool> child при его фактическом выполнении или сохранить его значение впоследствии, это было бы прекрасно. Я поигрался с метаданными CacheMetadata, чтобы посмотреть, смогу ли я найти что-нибудь, что позволило бы мне это сделать, но пока ничего не нашел.

Обновление 2

Проблема, очевидно, исходит от ActivityFunc <bool> Condition. Я собираюсь пройти и проверить, какие могут быть проблемы с условием. Не уверен, стоит ли переходить к новому вопросу или нет, поскольку он технически не решен, но я посмотрю, как составить тестовое Условие, из которого можно выйти, и если больше ничего не покажет, где я.

Обновление 3

Хорошо, это простой пример того, что я использую в качестве дочернего действия, которое всегда возвращает false, даже если в ходе выполнения оно принимает значение true.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using System.Activities.Presentation;
using System.ComponentModel;
using System.Drawing;

namespace SomeActivities
{
    public sealed class DataHandlerTypeName : NativeActivity,IActivityTemplateFactory
    {
        // Define an activity input argument of type string
        [RequiredArgument]
        public InArgument ImportContext { get; set; }

        /// 
        /// Gets or sets the handler type name to check.
        /// 
        /// The handler type name to check.
        [RequiredArgument]
        public string HandlerTypeNameToCheck { get; set; }


        /// 
        /// Performs the trigger check for the matching Data Type Handler Names
        /// 
        /// The context.
        protected override void Execute(NativeActivityContext context)
        {
            var ic = this.ImportContext.Get(context);

            if (1==1)
            {
                //context.SetValue(base.Result, true);
                Result.Set(context, true);
            }
            else 
            {
                //context.SetValue(base.Result, true);
                Result.Set(context, false);
            }
        }

        #region IActivityTemplateFactory Members


        Activity IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
        {
            return new DataHandlerTypeName()
            {
                ImportContext = this.ImportContext,
                HandlerTypeNameToCheck = "Default"
            };
        }

        #endregion
    }
}


Ответы [ 2 ]

1 голос
/ 21 октября 2010

Здравствуйте, кто-то, кого я никогда раньше не встречал и просто делится своим IP.

Вы делаете что-то не так. В другом месте это.

Либо DLL с используемыми вами действиями не является новой, либо определение рабочего процесса устарело и не содержит того, что, как вы считаете, оно делает. Или это что-то совершенно другое.

Я сократил ваш код и сжал его до примера проекта. Хотелось бы увидеть это здесь мы идем:

Это простое условие:

public sealed class AnTrigger : NativeActivity<bool>
{
    public bool ResultToSet { get; set; }

    protected override void Execute(NativeActivityContext context)
    {
        Result.Set(context, ResultToSet);
    }
}

Просто, нет? Вот хост, который оценивает это условие и, если он возвращает true, выполняет одно дочернее действие. Обратите внимание, я создаю действие в методе Create, поэтому мне не нужно создавать редактор.

public sealed class AnTriggerHost : NativeActivity, IActivityTemplateFactory
{
    public ActivityFunc<bool> Condition { get; set; }
    public ActivityAction Child { get; set; }

    protected override void Execute(NativeActivityContext context)
    {
        context.ScheduleFunc(Condition, OnConditionComplete);
    }

    private void OnConditionComplete(
        NativeActivityContext context, 
        ActivityInstance completedInstance, 
        bool result)
    {
        if (result)
            context.ScheduleAction(Child);
    }

    Activity IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
    {
        // so I don't have to create UI for these, here's a couple samples
        // seq is the first child and will run as the first AnTrigger is configured to return true
        // so the first trigger evals to true and the first child runs, which
        var seq = new Sequence
        {
            DisplayName = "Chief Runs After First Trigger Evals True"
        };
        // prints this message to the console and
        seq.Activities.Add(new WriteLine { Text = "See this?  It worked." });
        // runs this second trigger host, which 
        seq.Activities.Add(
            new AnTriggerHost
            {
                DisplayName = "This is the SECOND host",
                Condition = new ActivityFunc<bool>
                {
                    // will NOT be triggered, so you will never see
                    Handler = new AnTrigger
                    {
                        ResultToSet = false,
                        DisplayName = "I return false guize"
                    }
                },
                Child = new ActivityAction
                {
                    // this activity write to the console.
                    Handler = new WriteLine
                    {
                        Text = "you won't see me"
                    }
                }
            });

        return new AnTriggerHost
        {
            DisplayName = "This is the FIRST host",
            Condition = new ActivityFunc<bool>
            {
                Handler = new AnTrigger
                {
                    ResultToSet = true,
                    DisplayName = "I return true!"
                }
            },
            Child = new ActivityAction
            {
                Handler = seq
            }
        };
    }
}

Перетащите эти два в приложение Workflow Console и поместите AnTriggerHost в рабочий процесс. Установите пару контрольных точек и наблюдайте, как они летят. Вот рабочий процесс xaml:

  <local:AnTriggerHost DisplayName="This is the FIRST host" >
    <local:AnTriggerHost.Child>
      <ActivityAction>
        <Sequence DisplayName="Chief Runs After First Trigger Evals True">
          <WriteLine Text="See this?  It worked." />
          <local:AnTriggerHost DisplayName="This is the SECOND host">
            <local:AnTriggerHost.Child>
              <ActivityAction>
                <WriteLine Text="you won't see me" />
              </ActivityAction>
            </local:AnTriggerHost.Child>
            <local:AnTriggerHost.Condition>
              <ActivityFunc x:TypeArguments="x:Boolean">
                <local:AnTrigger DisplayName="I return false guize" ResultToSet="False" />
              </ActivityFunc>
            </local:AnTriggerHost.Condition>
          </local:AnTriggerHost>
        </Sequence>
      </ActivityAction>
    </local:AnTriggerHost.Child>
    <local:AnTriggerHost.Condition>
      <ActivityFunc x:TypeArguments="x:Boolean">
        <local:AnTrigger DisplayName="I return true!" ResultToSet="True" />
      </ActivityFunc>
    </local:AnTriggerHost.Condition>
  </local:AnTriggerHost>

Ваша проблема не в действиях, а в том, как вы их используете. Вы предполагаете, что ваша испытательная установка правильна, когда это не так.


Для дальнейшего использования ... Когда это происходит со мной, причина в том, что я установил Result в методе Create IActivityTemplateFactory

Activity IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
{
    return new Child
    {
        InputText = new InArgument<string>(
            new VisualBasicValue<string>(Parent.InputTextVariable)),
        // the following demonstrates what NOT to do in the Create method. 
        // this BREAKS your ActivityFunc, which will ALWAYS return default(T)
        // DO NOT SET Result AT ANY TIME OR IN ANY PLACE
        // BEGIN ERROR
        Result = new OutArgument<string>()
        // END ERROR
    };
}

В результате получается набор результатов в определении рабочего процесса, который нарушает шаблон ActivityFunc.

<!--If you see ActivityFunc.Result in your workflow, DELETE IT -->
<ActivityFunc.Result>
  <DelegateOutArgument x:TypeArguments="x:String" />
</ActivityFunc.Result>
1 голос
/ 20 октября 2010

ScheduleFunc всегда принимает ActivityFunc, где ваше состояние определяется как ActivityFunc.Не уверен, откуда исходит не универсальный ActivityFunc.Также CompletionCallback должен быть CompletionCallback.

Обновление: код теста, который я использовал:

IActivityTemplateFactory factory = new Trigger();
var trigger = (Trigger)factory.Create(null);
trigger.Condition.Handler = new AlwaysTrue();
trigger.Child.Handler = new WriteLine()
{
    Text = "Its true."
};
WorkflowInvoker.Invoke(trigger);

class AlwaysTrue : CodeActivity<bool>
{
    protected override bool Execute(CodeActivityContext context)
    {
        return true;
    }
}

И IActivityTemplateFactory.Create:

Activity IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
{
    return new Trigger()
    {
        Child = new ActivityAction()
        {
            DisplayName = "Trigger Child Action"
        },

        Condition = new ActivityFunc<bool>()
        {
            DisplayName = "Trigger Conditionals"
        },
        DisplayName = "Trigger"
    };
}
...