1
votes

I have a well defined service contract which exposes a bunch of methods. We have a typical service implementation of this contract which is hosted within IIS 7 along with our MVC App.

The architecture is of a typical distributed application with the interface defined in a base core library (which is re-distributable), the implementation within an independent services library and finally the MVC App exposing an end-point for the implementation(which is within the services library).

The situation now is that one of these existing service methods needs to possibly execute a logical process which can take upto 10 minutes to execute. In a normal scenario we would have considered workflow services but the interface under question is well in use, we have a suite of unit tests to test our services etc. and we really can't get away with this implementation that we have.


So my questions are -

  1. Is it possible to have an independent workflow which can do this long running process and call it from our WCF service?
  2. If it is then how do I ensure that the worker thread executing my service within IIS will stay alive for the duration for the workflow?
  3. Finally the client does not need to wait for a response from this service. It is a fire and forget method. Can the client call end immediately while the service kicks off a workflow and waits for it to finish?
1

1 Answers

6
votes
  1. Sure. In your WCF service you'll use a WorkflowApplication to execute your workflow definition. This will take care of executing/tracking each WF instances with the WF runtime on WF runtime specific threads (i.e. not blocking a WCF I/O thread).
  2. There are no guarantees here. If the app pool crashes or is scheduled to shut down due to "inactivity" that will terminate any workflows that are still potentially executing. You will have to use persistence points to ensure that if the WF is terminated you will resume from previous bookmarks.
  3. Yes, first, mark your WCF service method with [OperationContract(IsOneWay=true)]. Second you will use the WorkflowApplication instance from 1 to start the WF using the async BeginRun and, if you care about tracking completion/errors in your WCF service, you can register the necessary Aborted, Completed, Unloaded handlers.

Here's a really simple example:

[DataContract]
public class MyParametersDataContract
{
   [DataMember(Order=1, IsRequired=true)]
   public string SomeValue
   {
       get;
       set;
   }
}

public class IMyService
{
    [OperationContract(IsOneWay=true)]
    void DoSomething(MyParametersDataContract someParameters);
}

public class MyService : IMyService
{
     // Hold your WF definition in a static singleton to reduce overhead of activity realization
     public static readonly Lazy<MyFancyWorkflow> MyWorkflow = Lazy<MyFancyWorkflow>(() => new MyFancyWorkflow());

     public void DoSomething(MyParametersDataContract someParameters)
     {
         // Example of translating incoming parameters to WF args
         Dictionary<string, object> workflowInputArguments = new Dictionary<string, object>
         {
             { "SomeArgument", someParameters.SomeValue }
         };

         // Create a WFA instance for this request
         WorkflowApplication workflowApplication = new WorkflowApplication(MyService.MyWorkflow.Value, workflowInputArguments);

         // Example of hooking the completed action
         workflowApplication.Completed = (workflowCompletedArgs) =>
         {
             // do something when workflow has completed
         };

         // Invoke the running of the WF asynchronously
         Task.Factory.FromAsync(
                                workflowApplication.BeginRun,
                                workflowApplication.EndRun,
                                null)
                               .ContinueWith(runAntecdent =>
                               {
                                  // Observe/log any exception with starting the WF so it doesn't crash the process
                                  Trace.TraceWarning("WF failed to run: " + runAntecedent.Exception.ToString());
                               },
                               TaskContinuationOptions.OnlyOnFaulted);
     }
}