WCF Workflow Service: ForEach<>: Iterating over an Input Array to create an Output Array

Answered WCF Workflow Service: ForEach<>: Iterating over an Input Array to create an Output Array

  • 2012年4月13日 20:22
     
     

    Hi

    I am creating a very simple WCF Workflow Service, call it BookInventory.xamlx

    Within Workflow Designer, I want to create a WCF Workflow Service that processes an array of BookSearch and returns an array of BookInfo which is a result of each BookSearch proccessed by a "Code Activity" ProcessBook (In BookSearch, Out BookInfo).

    The ReceiveRequest is receiving (Input) an array: BookSearch[]

    The SendResponse is returning (Output) an array: BookInfo[]

    Between ReceiveRequest and SendResponse is a Sequence, and within its body a ForEach<BookSearch>.

    Each itme wtihin BookSearch[] will be iterated over using ForEach<BookSearch> , and I want to build up the result array BookInfo[] with search results of each processed item.

    I have created a "Code Activity" ProcessBook that is within the Body of ForEach<BookSearch> which inputs a BookSearch and outputs a BookInfo.

    Question: It is not clear to me how to complete the bodies of the Sequence and ForEach<BookSearch> on how to incrementally build up the resulting array BookInfo[]?

    I have noticed that WCF Workflow Service has Variables but not Arguments (why)?

    Thanks

    Jeff in Seattle


    Thanks
    Jeff in Seattle



全部回复

  • 2012年4月15日 18:12
     
     

    Hi Jeff,

    in order to build the resulting array, you must initialize it before the Foreach<BookSearch> activity, and after completing the ProcessBook Code activity you must add an activity of type "AddToCollection<T>" to add the result to the collection you want to return.

    About accepting arguments if you think it makes sense for xamlx not accept them, after all a client application will be consuming a service, not invoking a workflow! Why would you want arguments? You can always send a soap header to the xamlx if you want to send always some "common request" information.

    Hope this helps.

  • 2012年4月15日 19:02
     
     

    Thanks for your reply

    Using Workflow Designer: How would initialize the resulting BookInfo[] array, using the Assign component or as a Variable of the Sequence component?

    Thanks

    Jeff in Seattle


    Thanks
    Jeff in Seattle

  • 2012年4月15日 20:15
     
     
    It depends on the scope of your variable... if you only want to use it inside the sequence component or not. I believe you want to use it outside the sequence component to retrieve your results back to the client application, so, as I mentioned on my last post, you should assign the BookInfo[] before the Foreach activity (using the assign component).
  • 2012年4月16日 2:09
     
      包含代码

    It seems to go fine except when my WF design gets to AddToCollection<>

    The declared variables are:

        <p1:Sequence.Variables>
          <p1:Variable x:TypeArguments="CorrelationHandle" Name="handle" />
          <p1:Variable x:TypeArguments="b:BookSearch[]" Name="searches" />
          <p1:Variable x:TypeArguments="b:BookInfo" Name="result" />
          <p1:Variable x:TypeArguments="b:BookInfo[]" Name="results" />
          <p1:Variable x:TypeArguments="x:Int32" Name="count" />
        </p1:Sequence.Variables>

    The WCF Workflow Receive is defined as:

        <Receive x:Name="__ReferenceID0" CanCreateInstance="True" DisplayName="ReceiveRequest" sap:VirtualizedContainerService.HintSize="287,90" OperationName="LookupBooks3" ServiceContractName="p:IBookInventory">
          <Receive.CorrelationInitializers>
            <RequestReplyCorrelationInitializer CorrelationHandle="[handle]" />
          </Receive.CorrelationInitializers>
          <ReceiveParametersContent>
            <p1:OutArgument x:TypeArguments="b:BookSearch[]" x:Key="Searches">[searches]</p1:OutArgument>
          </ReceiveParametersContent>
        </Receive>

    I get the count of the number of BookSearch[]:

        <p1:Assign sap:VirtualizedContainerService.HintSize="287,58">
          <p1:Assign.To>
            <p1:OutArgument x:TypeArguments="x:Int32">[count]</p1:OutArgument>
          </p1:Assign.To>
          <p1:Assign.Value>
            <p1:InArgument x:TypeArguments="x:Int32">[searches.Length]</p1:InArgument>
          </p1:Assign.Value>
        </p1:Assign>

    Then I create a result array:

        <p1:Assign sap:VirtualizedContainerService.HintSize="287,58">
          <p1:Assign.To>
            <p1:OutArgument x:TypeArguments="b:BookInfo[]">[results]</p1:OutArgument>
          </p1:Assign.To>
          <p1:Assign.Value>
            <p1:InArgument x:TypeArguments="b:BookInfo[]">[New BookInfo(count) {}]</p1:InArgument>
          </p1:Assign.Value>
        </p1:Assign>

    Then I apply the search to the ForEach<> loop, but it breaks with the AddToCollection<> :

        <p1:ForEach x:TypeArguments="b:BookSearch" DisplayName="ForEach&lt;BookSearch&gt;" sap:VirtualizedContainerService.HintSize="287,314" Values="[searches]">
          <p1:ActivityAction x:TypeArguments="b:BookSearch">
            <p1:ActivityAction.Argument>
              <p1:DelegateInArgument x:TypeArguments="b:BookSearch" Name="item" />
            </p1:ActivityAction.Argument>
            <p1:Sequence sap:VirtualizedContainerService.HintSize="257,208">
              <sap:WorkflowViewStateService.ViewState>
                <scg3:Dictionary x:TypeArguments="x:String, x:Object">
                  <x:Boolean x:Key="IsExpanded">True</x:Boolean>
                </scg3:Dictionary>
              </sap:WorkflowViewStateService.ViewState>
              <b:PerformLookup4 sap:VirtualizedContainerService.HintSize="200,22" Result="[result]" Search="[item]" />
              <p1:AddToCollection x:TypeArguments="b:BookInfo" Collection="[results]" DisplayName="AddToCollection&lt;BookInfo&gt;" sap:VirtualizedContainerService.HintSize="200,22" Item="[result]" />
            </p1:Sequence>
          </p1:ActivityAction>
        </p1:ForEach>

    Did I declare the [results] used by AddToCollection<> incorrectly? It runs fine (with no results) without the AddToCollection<> within Sequence within the ForEach<> body.

    Here is the error log:

    The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.
    
    Server stack trace: 
       at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
       at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
       at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
       at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
       at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
    
    Exception rethrown at [0]: 
       at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
       at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
       at IBookInventory.LookupBooks3(BookSearch[] Searches)
       at BookInventoryClient.LookupBooks3(BookSearch[] Searches)


    Thanks
    Jeff in Seattle

  • 2012年4月16日 7:34
    版主
     
     

    Hi,

    Could you please provide a sample project for us download or send it to me at: liangliang(dot)tang(at)hotmail.com. It will be convenience for us to reproduce this issue. Thanks.


    Leo Tang [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

  • 2012年4月16日 10:03
     
     

    It seems ok to me, but do you have any extra loggin/tracing applications?

    With the error description you've posted you won't get the real error! Are you using AppFabric since this is a xamlx?

    Can you check the tracked events for this service when this exception happens? It really helps you (and us:)

  • 2012年4月16日 16:53
     
     

    Hi Leo Tang

    Sent you my WCF Workflow VS2010 solution. Tell me if you have any issues.

    Thank you


    Thanks
    Jeff in Seattle

  • 2012年4月16日 18:46
     
      包含代码

    Okay, I progressed further on my problem

    I am calling my WCF Workflow Service with the ForEach<> using a Workflow Console Application.

    The Workflow Console App's call to the WCF Workflow Service is wrapped with a TryCatch Workflow design component, and it is catching: "Exception: Collection was of a fixed size"

    The WCF Workflow Service's pseudo code initialization of collection BookInfo[] results was performed by:

    ReceiveRequest: In: BookSearch[] searches
    Assign: count = searches.Length
    Assign: results = BookInfo(count) {}
    
    ForEach<item> of searches
     {
        result = LookupBook(item)
        AddToCollection<result> to results
     }

    How am I initializing the collection "results" incorrectly, because AddToCollection<> is upset?

    Thanks


    Thanks
    Jeff in Seattle


  • 2012年4月17日 15:09
     
      包含代码

    I found the problem, do not use AddToCollection<> when the referred collection is an array.

    However, now I have a new problem: The sent response is returning an array whereby the last item is null.

    Here is the WCF Workflow Service's pseudo code:

    1. Create a results array that is the same size as the searches array (BookSearch[])
    2. Iterate over the searches array, lookup book info, then add book_info into results array (BookInfo[]).
    Assign: count = searches.Length
    Assign: results = BookInfo(count) {}
    Assign: i = 0
    
    ForEach<item> of searches
     {
        result = LookupBook(item)
        Assign: results(i) = result
        Assign: i = i + 1
     }

    For example: I am sending in an array of BookSearch (searches) of size 3, but response out is a BookInfo (results) array of size 4 whereby the last element in this array is null.

    Where is the extra BookInfo (of value null) coming from?


    Thanks
    Jeff in Seattle


  • 2012年4月18日 3:48
    版主
     
     已答复

    Hi,

    When declaring an array in Visual Basic Expression, it is different with C#

    "New BookInfo(count) {}" means the BookInfo contains "count+1" elements, the indexes of the elements range from 0 through "count"

    http://msdn.microsoft.com/en-us/library/wak0wfyt.aspx


    Leo Tang [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

  • 2012年4月18日 4:10
     
     

    Thanks Leo Tang

    To avoid this confusion again with VB expressions, is there an expected future release of Workflow to accept C# expressions?


    Thanks
    Jeff in Seattle

  • 2012年4月18日 7:15
    版主
     
     

    Hi,

    WF4.5 has support for C# expression. Thanks.


    Leo Tang [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

  • 2012年4月19日 6:29
     
     

    Thanks Leo Tang

    I am not a VB programmer, but why would a programming language provide such a confusing array construction.

    I appreciate your assistance in this topic.


    Thanks
    Jeff in Seattle