服务总线工作流程活动

时间:2013-06-08 19:04:30

标签: workflow-foundation-4 async-await c#-5.0 azure-servicebus-queues

我想通过一些特定的活动访问工作流程中的服务总线队列和主题。

我找不到符合此方案的任何内容(this MSDN articlethis article by Roman Kiss)是最近的。

我想设计一个自定义活动,它使用QueueClient异步接收代理消息,使用使用async / await模式实现的BeginReceive方法(请参阅my question关于它)。

首先,我想问一下,为什么我更喜欢建议的方法(改编的WCF)而不是我想要的方法(使用QueueClient)。

然后,我将非常感谢以持久友好的方式设计它。

更新

这是我到目前为止所尝试的:

public class AsyncReceiveBrokeredMessage : AsyncCodeActivity<BrokeredMessage>
{
    [RequiredArgument]
    public InArgument<string> ConnectionString { get; set; }

    [RequiredArgument]
    public InArgument<string> Path { get; set; }

    protected sealed override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        var connectionString = this.ConnectionString.Get(context);
        var path = this.Path.Get(context);
        var queueClient = QueueClient.CreateFromConnectionString(connectionString, path);
        var cts = new CancellationTokenSource();
        context.UserState = new ReceiveState
                                {
                                    CancellationTokenSource = cts,
                                    QueueClient = queueClient
                                };
        var task = ExecuteAsync(context, cts.Token);
        var tcs = new TaskCompletionSource<BrokeredMessage>(state);
        task.ContinueWith(
            t =>
                {
                    if (t.IsFaulted)
                    {
                        tcs.TrySetException(t.Exception.InnerExceptions);
                    }
                    else if (t.IsCanceled)
                    {
                        tcs.TrySetCanceled();
                    }
                    else
                    {
                        tcs.TrySetResult(t.Result);
                    }

                    if (callback != null)
                    {
                        callback(tcs.Task);
                    }
                });

        return tcs.Task;
    }

    protected sealed override BrokeredMessage EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        var task = (Task<BrokeredMessage>)result;
        try
        {
            return task.Result;
        }
        catch (OperationCanceledException)
        {
            if (context.IsCancellationRequested)
            {
                context.MarkCanceled();
            }
            else
            {
                throw;
            }

            return null; // or throw?
        }
        catch (AggregateException exception)
        {
            if (exception.InnerException is OperationCanceledException)
            {
                if (context.IsCancellationRequested)
                {
                    context.MarkCanceled();
                }
                else
                {
                    throw;
                }

                return null; // or throw?
            }

            ExceptionDispatchInfo.Capture(exception.InnerException).Throw();
            throw;
        }
    }

    protected override void Cancel(AsyncCodeActivityContext context)
    {
        var state = (ReceiveState)context.UserState;
        state.CancellationTokenSource.Cancel();
    }

    private async Task<BrokeredMessage> ExecuteAsync(
        AsyncCodeActivityContext context, CancellationToken cancellationToken)
    {
        var receiveState = context.UserState as ReceiveState;
        var receiveTask = Task<BrokeredMessage>.Factory.FromAsync(
            receiveState.QueueClient.BeginReceive, receiveState.QueueClient.EndReceive, null);
        var completionTask = receiveTask.ContinueWith(
             t =>
                 {
                     BrokeredMessage result;
                     if (t.IsCanceled)
                     {
                         context.MarkCanceled();
                         result = null;
                     }
                     else if (t.IsFaulted)
                     {
                         result = null;
                     }
                     else
                     {

                         t.Result.Complete();
                         result = t.Result;
                     }

                     receiveState.QueueClient.Close();
                     return result;
                 },
             cancellationToken);
        return await completionTask;
    }

    private class ReceiveState
    {
        public CancellationTokenSource CancellationTokenSource { get; set; }

        public QueueClient QueueClient { get; set; }
    }
}

并以这种方式测试(使用本地Windows Server Service Bus):

var connectionString = new Variable<string>
                                   {
                                       Default = connectionStringValue
                                   };
        var path = new Variable<string>
                       {
                           Default = pathValue
                       };
        var test = new While
                       {
                           Body =
                               new Pick
                                   {
                                       Branches =
                                           {
                                               new PickBranch
                                                   {
                                                       Trigger =
                                                           new AsyncReceiveBrokeredMessage
                                                               {
                                                                   ConnectionString = new InArgument<string>(connectionString),
                                                                   Path = new InArgument<string>(path)
                                                               },
                                                       Action =
                                                           new WriteLine
                                                               {
                                                                   Text =
                                                                       "Received message"
                                                               }
                                                   },
                                               new PickBranch
                                                   {
                                                       Trigger =
                                                           new Delay
                                                               {
                                                                   Duration = TimeSpan.FromSeconds(10)
                                                               },
                                                       Action =
                                                           new WriteLine
                                                               {
                                                                   Text =
                                                                       "Timeout!"
                                                               }
                                                   }
                                           }
                                   },
                           Condition = true,
                           Variables = { connectionString, path }
                       };
        WorkflowInvoker.Invoke(test);

如果我不断发送消息,我会按预期收到消息。问题在第一次超时后出现,因为那时我没有收到任何消息。 任何澄清都表示赞赏。

3 个答案:

答案 0 :(得分:1)

首先,您需要了解一些重要的事情: 1)工作流程是长时间运行的过程,意味着可以在以后使用并可恢复。 2)工作流被唤醒和恢复的方式是书签。 3)通常人们喜欢他们的工作流程在暂停时也可以保持。 (如果你不关心持久性,你为什么还要使用WF - 只是为了视觉设计工具?)

逻辑问题:

如果您的所有工作流程及其活动都被保留并暂停,那么您的活动代码的 none 甚至已加载,那么谁在进行聆听?答案:别的东西,而不是活动,必须是在ServiceBus队列上侦听并负责恢复书签以唤醒工作流程的事情。

某些内容是工作流程&#39;主机&#39;或其某些扩展。以下是一些关于如何自定义主机以监听[来自GUI按钮]的消息并唤醒工作流活动的博客文章。

http://blogs.msdn.com/b/tilovell/archive/2011/02/26/wf4-workflow-4-0-hosting-extensions-redux.aspx

http://blogs.msdn.com/b/tilovell/archive/2010/06/08/wf4-workflow-4-0-hosting-extensions.aspx

你可以做的是获取此代码并使其适应于侦听ServiceBus队列而不是GUI按钮,并唤醒您自己的ReceiveFromServiceBus活动,这类似于PageActivity - 请注意您必须按顺序编写NativeActivity正确使用书签。

所有相当麻烦......但我相信“正确的”#39;用WF 的方法。

答案 1 :(得分:0)

队列实体提供以下功能:     “能够指定将消息添加到队列的时间。”

经过一段时间的超时后,由于这条规则,你可能无法收到?

5月决议是:

检测入站邮件重复项,允许客户端多次发送相同的邮件而不会产生不良后果。

答案 2 :(得分:0)

可能是DefaultMessageTimeToLive或TimeToLive属性的问题。

NamespaceManager.CreateSubscription(
        new SubscriptionDescription(TopicName, SubscriptionName)
            {
                LockDuration = TimeSpan.FromMinutes(5),
                DefaultMessageTimeToLive = TimeSpan.FromDays(7),
                EnableDeadLetteringOnMessageExpiration = true
            });