工作流基础 4 - ActivityFunc的结果即使执行方法显示 true,也始终为 false

发布于 2024-09-28 08:22:01 字数 4965 浏览 2 评论 0原文

Ello,我遇到了一个自定义活动的问题,该活动执行对“ActivityFunc”的评估并返回 false,即使它在 Execute 中评估为 true。 这是我的活动


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 方法中评估我的条件时,即使我将条件的结果打印为 true,它也会调用 OnConditionComplete 并返回结果(这始终为 false)。那么这里有什么我看不到的明显错误吗?

更新

好吧,我认为 Marice 谈论的是在类中拥有回调,并且只是让 OnConditionComplete 方法指向回调。我确实改变了这一点,但没有看到改变。如果我能以某种方式从 ActivityFunc检索值child 条件在其实际执行时或之后保存其值,这会很好用。我已经研究了 CacheMetadata 的元数据,看看是否有任何东西可以让我这样做,但到目前为止还没有找到任何东西。

更新 2

问题显然来自 ActivityFunc条件。我将不得不仔细检查并检查该条件可能存在哪些问题。不确定这是否应该涉及一个新问题,因为它在技术上尚未解决,但我会考虑将一个测试条件放在一起,如果没有其他东西显示我所处的位置。

Update 3

好的,这是我用作子活动的一个简单示例,即使它在执行中评估为 true,它也始终返回 false


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
    }
}


Ello, I'm having an issue with a custom activity that preforms an evaluation of a `ActivityFunc ` and returns false even though it is evaluated in Execute to be true.

Here is my activity


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,

            };
        }
    }
}

So when my condition is evaluated in the Execute Method it calls OnConditionComplete with the result (which is what is always false) even though if I print the result of the condition to be true. So is there something obviously wrong here that I don't see?

Update

Okay I think Marice talking about having the callback in the class and just having the OnConditionComplete method point to the callback. I did change that but haven't seen a change. If I could somehow retrieve the value from the ActivityFunc<bool> child condition when its actually executing or save its value afterward, that would work great. I've played around with CacheMetadata's metadata to see if there was anything that I could find that would allow me to do so but haven't found anything as of yet.

Update 2

The problem apparently is coming from the ActivityFunc <bool> Condition. I'm going to have to go through and check what the issue(s) with the Condition might be. Not sure if this should go to a new question or not since its technically not solved but I'll see about putting together a test Condition to go off of and if nothing else show where I'm at.

Update 3

Okay this is a bare bones example of what I use as a child activity that always returns false even though it evaluates to true in the execution


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
    }
}


如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

初懵 2024-10-05 08:22:01

你好,我以前从未见过,只是碰巧分享了我的 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
            }
        };
    }
}

将这两个放在工作流控制台应用程序中,并将 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>

您的问题不在于活动,而在于您如何使用它们。您假设您的测试设备是正确的,但事实并非如此。


供将来参考...当我遇到这种情况时,原因是我在 IActivityTemplateFactory 的 Create 方法中设置了 Result

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>

Hello someone who I have never met before and just happens to share my IP.

You're doing something wrong. Elsewhere, that is.

Either the DLL with the activities you're using isn't fresh, or the workflow definition is out of date and doesn't hold what you believe it does. Or its something completely different.

I've stripped down your code and compressed it into a sample project. Like to see it here we go:

This is the simple condition:

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

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

Simple, no? Here's the host that evaluates this condition and, if it returns true, runs a single child activity. Note, I'm constructing the activity in the Create method so I don't have to create an editor.

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
            }
        };
    }
}

Drop these two in a Workflow Console app and drop the AnTriggerHost on the workflow. Set ye a couple breakpoints and watch it fly. Here's the workflow 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>

Your issue doesn't lie in the activities, it lies in how you're using them. You're assuming your test rig is correct when it isn't.


For future reference... When this happens to me, the cause is that I set Result within the Create method of the 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
    };
}

This results in the result set within the workflow definition, which breaks the ActivityFunc pattern.

<!--If you see ActivityFunc.Result in your workflow, DELETE IT -->
<ActivityFunc.Result>
  <DelegateOutArgument x:TypeArguments="x:String" />
</ActivityFunc.Result>
累赘 2024-10-05 08:22:01

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"
    };
}

ScheduleFunc always takes a ActivityFunc where your condition is defined as ActivityFunc. Not sure where the non generic ActivityFunc comes from. Also the CompletionCallback should be a CompletionCallback.

Update:
The test code I used:

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;
    }
}

And the 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"
    };
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文