locked
Workflow Services in WF4 - how to find out which operations can be invoked? RRS feed

  • Question

  • Hi All

    I'm just working through a migration from WF3.5 to WF4.0.

    In WF3.5, with the Receive activity, I was able to access the workflow runtime behavior from the workflow service host, and get access to the workflow runtime. From the workflow runtime, I could get the workflow instance, and from the workflow instance I could enumerate the queues. Because the conventional queue name was InterfaceName|OperationName, I could find out which operations could be delivered to a particular workflow. I wrote a companion service that returned this information to the client. This meant that the client application didn't have to have knowledge of which operations could be delivered to a particular workflow based on its current state, which was nice!

    In WF4.0, is there equivalent functionality available?

    I can post some indicative code samples from WF3.5 if required...

    Regards

    Nigel
    Friday, September 18, 2009 10:31 AM

All replies

  • Hi Nigel,
    Unfortunately we don't have a way to do this with WF 4.0. And I don't know if the way you are implementing this in WF 3.5 is actually supported because we don't guarantee that the queue names will remain the same in future releases. The closest you could come would be to either use reflection on WorkflowServiceInstance to find the list of bookmarks (getting to the instance is not straightforward) or if the instance is persisted you can look in the database (but the data may be stale). Another possibility would be to hook into tracking at idle but that doesn’t provide any bookmark information.

    Go ahead and post your sample code...perhaps it will help to figure outa workaround for WF 4.0.

    -Amy
    Thursday, September 24, 2009 7:28 PM
  • Thanks for the reply Amy. Code follows at the end. I did always think that the WF3.0/3.5 API was lacking in this area. While you could access the current state name of a state machine workflow via the CurrentStateName, and which states you could go to via the PossibleStateTransitions, there was no way to ask how to get there! The problem with this is that the client code has to be written reimplementing the state transition logic, which means that if the workflow design changed, then the client code had to be altered as well. Too much coupling for my liking. I'd be happy to hear about alternative approaches which would enable me to do this in WF 4.0... Regards Nigel Anyhow, here's a minimal illustration of the approach I was discussing in VB. I've done some trimming outside of VS, so if it doesn't work as written, apologies... Imports System.ServiceModel Imports System.Workflow.Runtime Imports System.ServiceModel.Description Module Host Dim workflowHost As WorkflowServiceHost Dim reportHost As ServiceHost Dim workflowRuntime As WorkflowRuntime Sub Main() Console.WriteLine("Starting host...") workflowHost = New WorkflowServiceHost(GetType(WorkflowLibrary.SampleWorkflow)) workflowHost.Open() Console.WriteLine("Running...") Dim workflowRuntimeBehavior As WorkflowRuntimeBehavior = workflowHost.Description.Behaviors.Find(Of WorkflowRuntimeBehavior)() workflowRuntime = workflowRuntimeBehavior.WorkflowRuntime Console.WriteLine("Starting reporter...") reportHost = New ServiceHost(New ReportWorkflowOperations(workflowRuntime)) reportHost.Open() Console.WriteLine("Running...press a key to quit") Console.Read() workflowHost.Close() reportHost.Close() End Sub End Module _ Public Interface IReportWorkflowInformation _ Function AvailableOperations(ByVal interfaceName As String, ByVal workflowInstanceId As Guid) As String() End Interface _ Public Class ReportWorkflowOperations Implements WorkflowLibrary.IReportWorkflowInformation Private workflowRuntime As WorkflowRuntime Public Sub New(ByVal workflowRuntime As WorkflowRuntime) Me.workflowRuntime = workflowRuntime End Sub Public Function AvailableOperations(ByVal interfaceName As String, ByVal workflowInstanceId As Guid) As String() Implements WorkflowLibrary.IReportWorkflowInformation.AvailableOperations Dim workflowInstance As WorkflowInstance = Nothing Try workflowInstance = workflowRuntime.GetWorkflow(workflowInstanceId) Catch ex As Exception Console.WriteLine(ex.GetType().Name) End Try If workflowInstance Is Nothing Then Return New String() {} End If Dim queues = workflowInstance.GetWorkflowQueueData() Dim operations As New List(Of String) For Each item In queues Dim name = item.QueueName.ToString() If name.Contains(interfaceName) Then Dim startIndex = name.IndexOf("|") operations.Add(name.Substring(startIndex + 1)) End If Next Return operations.ToArray() End Function End Class
    Thursday, September 24, 2009 8:17 PM
  • Well that didn't come through very well.

    Hang on, and I'll try posting again from a different browser!
    Thursday, September 24, 2009 8:18 PM
  • Second try...

    Thanks for the reply Amy.

    Code follows at the end.

    I did always think that the WF3.0/3.5 API was lacking in this area. While you could access the current state name of a state machine workflow via the CurrentStateName, and which states you could go to via the PossibleStateTransitions, there was no way to ask how to get there!

    The problem with this is that the client code has to be written reimplementing the state transition logic, which means that if the workflow design changed, then the client code had to be altered as well. Too much coupling for my liking.

    I'd be happy to hear about alternative approaches which would enable me to do this in WF 4.0...

    Regards Nigel

    Anyhow, here's a minimal illustration of the approach I was discussing in VB.

    Imports

    System.ServiceModel

    Imports

    System.Workflow.Runtime

    Imports

    System.ServiceModel.Description

     

    Module

    Host

    Dim workflowHost As WorkflowServiceHost

    Dim reportHost As ServiceHost

    Dim workflowRuntime As WorkflowRuntime

     

     

     

     

    Sub Main()

    Console.WriteLine("Starting hosts...")

    workflowHost = New WorkflowServiceHost(GetType(WorkflowLibrary.SampleWorkflow))

    workflowHost.Open()

    Console.WriteLine("Running...")

    Dim workflowRuntimeBehavior As WorkflowRuntimeBehavior = workflowHost.Description.Behaviors.Find(Of WorkflowRuntimeBehavior)()

    workflowRuntime = workflowRuntimeBehavior.WorkflowRuntime

    Console.WriteLine("Starting reporter...")

    reportHost = New ServiceHost(New ReportWorkflowOperations(workflowRuntime))

    reportHost.Open()

    Console.WriteLine("Running...press a key to quit")

    Console.Read()

    workflowHost.Close()

    reportHost.Close()

    End Sub

    End

    Module

    <ServiceContract()> _

    Public

    Interface IReportWorkflowInformation

    <OperationContract()> _

    Function AvailableOperations(ByVal interfaceName As String, ByVal workflowInstanceId As Guid) As String()

    End

    Interface

     

     

    <ServiceBehavior(InstanceContextMode:=InstanceContextMode.Single)> _

    Public

    Class ReportWorkflowOperations

    Implements IReportWorkflowInformation

    Private workflowRuntime As WorkflowRuntime

    Public Sub New(ByVal workflowRuntime As WorkflowRuntime)

    Me.workflowRuntime = workflowRuntime

    End Sub

    Public Function AvailableOperations(ByVal interfaceName As String, ByVal workflowInstanceId As Guid) As String() Implements IReportWorkflowInformation.AvailableOperations

    Dim workflowInstance As WorkflowInstance = Nothing

    Try

    workflowInstance = workflowRuntime.GetWorkflow(workflowInstanceId)

    Catch ex As Exception

    Console.WriteLine(ex.GetType().Name)

    End Try

    If workflowInstance Is Nothing Then

    Return New String() {}

    End If

    Dim queues = workflowInstance.GetWorkflowQueueData()

    Dim operations As New List(Of String)

    For Each item In queues

    Dim name = item.QueueName.ToString()

    If name.Contains(interfaceName) Then

    Dim startIndex = name.IndexOf("|")

    operations.Add(name.Substring(startIndex + 1))

    End If

    Next

    Return operations.ToArray()

    End Function

    End

    Class

    Thursday, September 24, 2009 8:28 PM
  • It’s definitely reasonable to do that.  The problem is here:

    Dim queues = workflowInstance.GetWorkflowQueueData()

    Dim operations As New List(Of String)

    For Each item In queues

    Dim name = item.QueueName.ToString()

    If name.Contains(interfaceName) Then

    Dim startIndex = name.IndexOf("|")

    operations.Add(name.Substring(startIndex + 1))

    End If

    Next

    Return operations.ToArray()

     

    We don't guarantee that that is necessarily the format of the queue name which makes this solution “not supported”.  This will work right now, but we could (but likely wouldn’t) go change that as an internal implementation detail.
    Tuesday, October 6, 2009 10:14 PM
  • Yep, I'm aware this is unsupported. 

    But back to the original question...is there going to be a mechanism (official or otherwise) to do something similar in WF 4?

    Nigel
    Wednesday, October 7, 2009 9:42 PM
  • I would be very keen for such a feature too.

    Andrew

    Wednesday, October 7, 2009 9:58 PM
  • I'm following up with some people who know more than me, but I think that SqlWorkflowInstanceStore might save the active bookmarks in a queriable manner.  Again, the bookmark naming scheme is not guaranteed to stay the same across releases, but you should be able to use this information (which gets populated at each persistence point) to do something like what you were doing before.

    [UPDATE] I just got confirmation that SqlWorkflowInstanceStore saves this data in a queriable way.
    Wednesday, October 28, 2009 7:06 PM
  • Can you supply an sql query that would return available methods to invoke?
    udione
    Thursday, September 9, 2010 11:37 AM