WF4中的StateMachine

时间:2011-09-28 22:31:13

标签: c# workflow-foundation-4

首先是免责声明 - 我是WF的新手,我只使用WF4玩了3天 - 所以对我可能有的任何良性或蹩脚的问题道歉......

我正在尝试在我的项目中实现WF4状态机。以下是一些背景信息:

  1. 我将WinForm UI作为可以为网络堆栈生成不同命令的主应用程序
  2. 我将接口层作为从网络堆栈接收事件的DLL
  3. 状态机应该在与接口层平行的中间层中的某处实现软电话状态机。电话状态根据网络事件和用户操作而变化。某些用户操作只会导致网络命令,有些可能直接影响状态机。
  4. 我的界面层实现了一个对象Phone,所以我把这个对象作为状态机的主机。我将我的主界面对象WinSipItf传递给phone对象,并执行以下操作(WinSipItf对象位于另一个命名空间WinSipNetLib中):
  5. 代码:

    public void Initialize(WinSipNetLib.WinSipItf winSipItf, string hostname, string user, string password)
    {
        try
        {
            WinSipItf = winSipItf;
            var Itf = WinSipItf;
            var input = new Dictionary<string, object>
                        {
                            {"winSipItf", Itf}
                        };
            this.WorkFlowHostApp = new WorkflowApplication(new Activity1(), input)
            {
                Idle = this.OnWorkflowIdle,
                Completed = this.OnWorkflowCompleted
            };
    
            // Setup the Tracking to output in the debug window
            WorkFlowHostApp.Extensions.Add(new Tracker());
    
            this.WorkFlowHostApp.Extensions.Add(this);
    
            this.workflowBusy.Reset();
    
            Console.WriteLine("Starting Workflow");
            this.WorkFlowHostApp.Run();
        }
        catch (Exception ex)
        {
            Console.Write(ex);
        }
    }
    

    手机初始状态为Initial,它具有目标状态PhoneIdle的自动/空触发器。 OnEntrace到Initial状态我需要用网络堆栈执行几个Phone注册 - 注册一个回调(同步操作)并注册网络堆栈管理器(async)。所有这些功能都存在于我通过接口层中构建的API访问的较低级别的非托管代码中。注册成功后,我在接口层获得回调并触发事件。因此,我的注册活动看起来像这样:

    public sealed class Register : NativeActivity<Int32>
    {
        // Define an activity input argument of type string
        [RequiredArgument]
        public InArgument<WinSipNetLib.WinSipItf> winSipItf { get; set; }
    
        #region Constants and Fields
    
        private readonly Variable<NoPersistHandle> _noPersistHandle = new Variable<NoPersistHandle>();
        internal const string BookmarkName = "WaitingForAccountStatusEvent";
    
        /// <summary>
        /// The transition callback.
        /// </summary>
        private BookmarkCallback registrationCallback;
    
        #endregion
    
        #region Properties
    
        ///// <summary>
        /////   Gets or sets PhoneTransition.
        ///// </summary>
        //public PhoneTransition PhoneTransition { get; set; }
    
        /// <summary>
        ///   Gets a value indicating whether CanInduceIdle.
        /// </summary>
        protected override bool CanInduceIdle
        {
            get
            {
                return true;
            }
        }
    
        /// <summary>
        /// Gets TransitionCallback.
        /// </summary>
        private BookmarkCallback RegistrationCallback
        {
            get
            {
                return this.registrationCallback ?? (this.registrationCallback = new BookmarkCallback(this.OnRegistrationCallback));
            }
        }
    
        #endregion
    
        #region Methods
    
        /// <summary>
        /// The cache metadata.
        /// </summary>
        /// <param name="metadata">
        /// The metadata.
        /// </param>
        protected override void CacheMetadata(NativeActivityMetadata metadata)
        {
            metadata.RequireExtension(typeof(Tracker));
            metadata.RequireExtension(typeof(Phone));
            metadata.RequireExtension(typeof(WaitForRegistrationExt));
            // !!! and one more for GUI
    
            // Provide a Func<T> to create the extension if it does not already exist
            metadata.AddDefaultExtensionProvider(() => new WaitForRegistrationExt());
    
            metadata.AddArgument(new RuntimeArgument("winSipItf", typeof(WinSipNetLib.WinSipItf), ArgumentDirection.In, true));
            metadata.AddArgument(new RuntimeArgument("Result", typeof(int), ArgumentDirection.Out, false));
    
            metadata.AddImplementationVariable(_noPersistHandle);
        }
    
        protected override void Execute(NativeActivityContext context)
        {
            // Enter a no persist zone to pin this activity to memory since we are setting up a delegate to receive a callback
            var handle = _noPersistHandle.Get(context);
            handle.Enter(context);
    
            // Get Phone extension needed to call the functions in WinSipItf
            Phone phone = (Phone)context.GetExtension<Phone>();
    
            // Get (which may create) the extension
            WaitForRegistrationExt regExt = context.GetExtension<WaitForRegistrationExt>();
    
            // Add the callback
            regExt.AddRegistrationCallback(winSipItf.Get(context));
    
            bool bRet = phone.WinSipItf.RegisterDBusCallback("WinSipPhone");
    
            // Obtain the runtime value of the Text input argument
            if (bRet == false)
            {
                this.Result.Set(context, bRet.ToString());
                return;
            }
    
            string hostname = "demo2.demo.com";
            string username = "406";
            string password = "123123"; 
            string id = username + "@" + hostname;
            String regUri = hostname;
            if (phone.WinSipItf.DoSipRegister(id, regUri, username, password) == 0)
            {
                this.Result.Set(context, bRet.ToString());
                return;
            }
    
            // Set a bookmark - the extension will resume when the Gizmo is fired
            context.CreateBookmark(BookmarkName, RegistrationCallback);
    
            //context.CreateBookmark(this.PhoneTransition.ToString(), this.RegistrationCallback);
    
            //// Obtain the runtime value of the Text input argument
            //string text = context.GetValue(this.Text);
    
            //Result.Set(context, string.Format("The text is {0}", text));
        }
    
        /// <summary>
        /// The on transition callback.
        /// </summary>
        /// <param name="context">
        /// The context.
        /// </param>
        /// <param name="bookmark">
        /// The bookmark.
        /// </param>
        /// <param name="value">
        /// The value.
        /// </param>
        /// <exception cref="InvalidOperationException">
        /// </exception>
        private void OnRegistrationCallback(NativeActivityContext context, Bookmark bookmark, object value)
        {
            if (value is WinSipItf.MSGDATA)
            {
    
            }
            ////if (value is StateTransitionResult)
            //{
            //    this.Result.Set(context, value as StateTransitionResult);
            //}
            //else if (value != null)
            //{
            //    // Resumed with something else
            //    throw new InvalidOperationException(
            //        "You must resume PhoneTransition bookmarks with a result of type StateTransitionResult");
            //}
    
            // Exit the no persist zone 
            var handle = _noPersistHandle.Get(context);
            handle.Exit(context);
        }
    
        #endregion
    }
    

    正如您从代码中看到的那样,我制作了像phone.WinSipItf.DoSipRegister这样的cals 有效吗?我认为所有状态机都在与WinSipItf对象构造的线程不同的线程上执行... 多余的说我不知道​​这个...我甚至无法在Execute或CacheMetadata中破解。不知道此时该怎么做。 值得一提的是我的xaml图是完全构建的,但是我试图通过构造这个自定义活动来实现Initial状态并将其转换为Idle状态......

    也许我已经开始从一个简单的问题开始了:我得到“必须活动参数的值'winSipItf'未被提供”警告当我将我的Register活动放在初始状态条目上时。我检查了所有代码,我不明白为什么。我错过了什么?

2 个答案:

答案 0 :(得分:3)

首先,由于您不熟悉Workflow,因此您可能需要执行Introduction to State Machine Hands On Lab

第二 - 我有一个示例应用程序,演示如何支持回调(是的,工作流正在另一个线程上执行)Windows Workflow Foundation (WF4) - Activity Callbacks and Event Handlers

答案 1 :(得分:0)

您没有添加工作流定义,但错误“未提供所需活动参数的值'winSipItf'”似乎是未设置Register活动的winSipItf参数的结果。它被标记为必需,因此需要使用工作流中的VB表达式进行设置。您正在向工作流提供名称为winSipItf的参数,但该参数与worklfow参数相关联,并且不会传播到包含具有相同参数名称的活动。