Windows Workflow 4 - 何时发出跟踪记录?

发布于 2024-10-21 22:53:33 字数 2650 浏览 5 评论 0原文

我有一个长时间运行的活动(该活动在很长一段时间内持续工作 - 不是正在等待外部源响应的活动),并且我想报告该活动的进度活动。但是,我无法让它发挥作用。

我有一个运行该活动的简单控制台应用程序:

class Program
{
    static void Main(string[] args)
    {
        var wfApp = new WorkflowApplication(new ActivityLibrary.ProgressActivity());

        var autoResetEvent = new AutoResetEvent(false);
        wfApp.Completed = e => autoResetEvent.Set();
        wfApp.Extensions.Add(new ConsoleTrackingParticipant());
        wfApp.Run();
        autoResetEvent.WaitOne();

        Console.WriteLine("Done");
        Console.ReadLine();
    }
}

internal class ConsoleTrackingParticipant : TrackingParticipant
{
    protected override void Track(TrackingRecord record, TimeSpan timeout)
    {
        Console.WriteLine(record.EventTime.ToString());
    }
}

我尝试以两种方式实现 ProgressActivity。首先,我尝试从 CodeActivity 派生,但是当我使用此实现时,一旦工作流程完成,我就会一起收到所有自定义跟踪记录(尽管报告的 record.EventTime 是正确的):

public sealed class ProgressActivity : CodeActivity
{
    protected override void Execute(CodeActivityContext context)
    {
        for (var i = 0; i <= 10; i++)
        {
            var customTrackingRecord = new CustomTrackingRecord("ProgressTrackingRecord")
                {
                    Data = { { "Progress", i * 10.0 }}
                };

            context.Track(customTrackingRecord);
            Thread.Sleep(1000);
        }
    }
}

然后我尝试从 AsyncCodeActivity 派生,但在这种情况下我得到context.Track 行上的“ObjectDisposeException:ActivityContext 只能在它传入的函数范围内访问”:

public sealed class ProgressActivity : AsyncCodeActivity
{
    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        Action action = () =>
            {
                for (var i = 0; i <= 10; i++)
                {
                    var customTrackingRecord = new CustomTrackingRecord("ProgressTrackingRecord")
                    {
                        Data = { { "Progress", i * 10.0 } }
                    };

                    context.Track(customTrackingRecord);
                    Thread.Sleep(1000);
                }
            };

        context.UserState = action;
        return action.BeginInvoke(callback, state);
    }

    protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        ((Action)context.UserState).EndInvoke(result);
    }
}

任何人都可以解释我哪里出了问题吗?

PS 我意识到,如果工作流程在服务器上运行,我所展示的方法将无法很好地扩展,但我正在构建的应用程序是桌面应用程序。

[编辑] 我想我实际上在这里问了一个更广泛的问题:工作流执行引擎何时发出跟踪记录?我的调查表明,特定活动的所有记录都会在活动完成后发出。有没有什么方法可以强制它们在活动执行期间被发射?

I have a long running activity (where the activity is continually doing work for a long period of time - not an activity that is waiting for a response from an external source) and I want to report progress from that activity. However, I cannot get this to work.

I have a simple console app that runs the activity:

class Program
{
    static void Main(string[] args)
    {
        var wfApp = new WorkflowApplication(new ActivityLibrary.ProgressActivity());

        var autoResetEvent = new AutoResetEvent(false);
        wfApp.Completed = e => autoResetEvent.Set();
        wfApp.Extensions.Add(new ConsoleTrackingParticipant());
        wfApp.Run();
        autoResetEvent.WaitOne();

        Console.WriteLine("Done");
        Console.ReadLine();
    }
}

internal class ConsoleTrackingParticipant : TrackingParticipant
{
    protected override void Track(TrackingRecord record, TimeSpan timeout)
    {
        Console.WriteLine(record.EventTime.ToString());
    }
}

I've tried implementing the ProgressActivity in two ways. First I tried deriving from CodeActivity, but when I use this implementation I receive all the custom tracking records together once the workflow has completed (though the reported record.EventTime is correct):

public sealed class ProgressActivity : CodeActivity
{
    protected override void Execute(CodeActivityContext context)
    {
        for (var i = 0; i <= 10; i++)
        {
            var customTrackingRecord = new CustomTrackingRecord("ProgressTrackingRecord")
                {
                    Data = { { "Progress", i * 10.0 }}
                };

            context.Track(customTrackingRecord);
            Thread.Sleep(1000);
        }
    }
}

I then tried deriving from AsyncCodeActivity, but in this case I get an "ObjectDisposedException: An ActivityContext can only be accessed within the scope of the function it was passed into" on the context.Track line:

public sealed class ProgressActivity : AsyncCodeActivity
{
    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        Action action = () =>
            {
                for (var i = 0; i <= 10; i++)
                {
                    var customTrackingRecord = new CustomTrackingRecord("ProgressTrackingRecord")
                    {
                        Data = { { "Progress", i * 10.0 } }
                    };

                    context.Track(customTrackingRecord);
                    Thread.Sleep(1000);
                }
            };

        context.UserState = action;
        return action.BeginInvoke(callback, state);
    }

    protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        ((Action)context.UserState).EndInvoke(result);
    }
}

Can anyone explain where I've gone wrong?

P.S. I realise that the approach I've shown would not scale well if the workflow was ever to run on a server, but the application I'm building is a desktop application.

[EDIT]
I guess I'm actually asking a broader question here: when are tracking records emitted by the workflow execution engine? My investigations suggest that all the records for a specific activity are emitted after the activity has completed. Is there any way to force them to be emitted during the execution of the activity?

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

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

发布评论

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

评论(3

茶底世界 2024-10-28 22:53:33

这可能是 TrackingParticipant 的副作用...尝试不扩展 TrackingParticipant 并看看这是否会产生影响。

工作流扩展不必扩展任何基类或实现任何接口。任何对象都可以是扩展。

我已经制作了不从任何内容扩展的扩展,并且它们在工作流程的生命周期内按照您的预期运行。

It might be a side behavior of TrackingParticipant... Try NOT extending TrackingParticipant and see if that makes a difference.

Workflow extensions do not have to extend any base class or implement any interface. Any object can be an extension.

I have made extensions that don't extend from anything, and they operate as you expect during the lifetime of the workflow.

画中仙 2024-10-28 22:53:33

我还在 MSDN 工作流论坛上提出了这个问题,并根据在那里收到的评论自己做了一些进一步的调查。因此,我认为当活动完成被添加书签时,就会发出跟踪记录。请参阅 http://social。 msdn.microsoft.com/Forums/en-US/wfprerelease/thread/8ce5dee9-9a19-4445-ad7f-33e181ec228b 了解更多信息。

I've also asked this question on the MSDN workflow forum, and done some further investigation myself based on the comments I received there. As a result I think that tracking records are emitted when the activity completes or is bookmarked. See http://social.msdn.microsoft.com/Forums/en-US/wfprerelease/thread/8ce5dee9-9a19-4445-ad7f-33e181ec228b for more info.

等风来 2024-10-28 22:53:33

使用 AsyncCodeActivity 允许我在活动启动时强制处理跟踪记录。

protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        CustomTrackingRecord customRecord = new CustomTrackingRecord("Start")
        {
            Data =
            {
            {"Date", DateTime.Now.ToShortDateString()},
            }
        };

        context.Track(customRecord);


        var executeDelegate = new Action(FakeWork);
        context.UserState = executeDelegate;
        return executeDelegate.BeginInvoke(callback, state);

    }

    private void FakeWork() {
        // do nothing
        // This allows the CustomTrackingRecord to be handled by the WorkFlowTracker 
        // so that we can identify the start of the activity.
        // The real work is done synchronously in the EndExecute method.
    }

    protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result) {

        var executeDelegate = (Action)context.UserState;
        executeDelegate.EndInvoke(result);

        Execute(context);
    }

    protected  void Execute(CodeActivityContext context)
    {
       // Do Work
    }

Using an AsyncCodeActivity allowed me to force the tracking record to be processed when the activity starts.

protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        CustomTrackingRecord customRecord = new CustomTrackingRecord("Start")
        {
            Data =
            {
            {"Date", DateTime.Now.ToShortDateString()},
            }
        };

        context.Track(customRecord);


        var executeDelegate = new Action(FakeWork);
        context.UserState = executeDelegate;
        return executeDelegate.BeginInvoke(callback, state);

    }

    private void FakeWork() {
        // do nothing
        // This allows the CustomTrackingRecord to be handled by the WorkFlowTracker 
        // so that we can identify the start of the activity.
        // The real work is done synchronously in the EndExecute method.
    }

    protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result) {

        var executeDelegate = (Action)context.UserState;
        executeDelegate.EndInvoke(result);

        Execute(context);
    }

    protected  void Execute(CodeActivityContext context)
    {
       // Do Work
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文