Reputation: 14525
I am trying to achieve the following:
A simple use case would be: a workflow for reviewing a document and either approving it or rejecting it. A workflow would be created for that matter, individuals would be notified, and, whenever they wanted, they could provide their feedback, by approving or rejecting the review.
I used as an example, the code from Andrew Zhu, available at http://xhinker.com/post/WF4.aspx.
The problems I have are:
WaitForRunnableInstance
waits indefinitely, so I am never notified that my workflow has started and was persisted to the database;BlockingBookmarks
set to the id of my custom activity, ExecutionStatus
set to Idle, IsInitialized
set to 1, IsSuspended
set to 0, IsCompleted
set to 0 and IsReadyToRun
set to 0.I already started a discussion on Microsoft Forums, that can be seen at http://social.msdn.microsoft.com/Forums/en-US/wfprerelease/thread/6262874d-3493-4be1-bd05-b990307e1875/ and got some feedback, but something is still not right.
Any ideas on this? Any useful patterns for long running workflows with custom activities?
Thanks!
Upvotes: 0
Views: 1703
Reputation: 7476
This is usually the smallest example of a long-running workflow, that waits for user input on console. (this code was never executed, take it as an example only)
/// Activity that waits on bookmark for
/// someone to send it some text
///
public sealed class ReadLine: NativeActivity<string>
{
[RequiredArgument]
public InArgument<string> BookmarkName { get; set; }
protected override bool CanInduceIdle
{
get
{
return true;
}
}
protected override void Execute(NativeActivityContext context)
{
context.CreateBookmark(
BookmarkName.Get(context),
new BookmarkCallback(OnReadComplete));
}
void OnReadComplete(NativeActivityContext context, Bookmark bookmark, object state)
{
context.SetValue(base.Result, state as string);
}
}
/// Program that uses ReadLine activity's bookmark to persist
/// workflow and waits for user input to resume it
///
public class Program
{
static InstanceStore InstanceStore;
static Activity Activity = GetExampleActivity();
static AutoResetEvent unloadEvent = new AutoResetEvent(false);
static Guid WfId;
static WorkflowApplication WfApp;
const string READ_LINE_BOOKMARK = "ReadLineBookMark";
static void Main()
{
CreateInstanceStore();
CreateWorkflowApp();
// Start workflow application and wait for input
StartAndUnload();
//Get user input and send it to ReadLine bookmark reviving workflow
GetInputAndComplete();
}
static void StartAndUnload()
{
WfApp.Run();
WfId = app.Id;
// !! Workflow will go idle on bookmark, no need to call Unload()
unloadEvent.WaitOne();
}
static void GetInputAndComplete()
{
var input = Console.ReadLine();
// We've the text input, let's resume this thing
WfApp.Load(WfId);
WfApp.ResumeBookmark(READ_LINE_BOOKMARK, input);
unloadEvent.WaitOne();
}
static void CreateInstanceStore()
{
InstanceStore = new SqlWorkflowInstanceStore("connection string");
var handle = InstanceStore.CreateInstanceHandle();
var view = InstanceStore.Execute(
handle,
new CreateWorkflowOwnerCommand(),
TimeSpan.FromSeconds(5));
handle.Free();
InstanceStore.DefaultInstanceOwner = view.InstanceOwner;
}
static void CreateWorkflowApp()
{
WfApp = new WorkflowApplication(Activity)
{
InstanceStore = InstanceStore,
};
WfApp.PersistableIdle = (e) => { return PersistableIdleAction.Unload; }
WfApp.Unloaded = (e) =>
{
Console.WriteLine("WF App Unloaded\n");
unloadEvent.Set();
};
WfApp.Completed = (e) =>
{
Console.WriteLine("\nWF App Ended: {0}.", e.CompletionState);
};
}
static Activity GetExampleActivity()
{
var response = new Variable<string>();
return return new Sequence()
{
Variables = { response },
Activities =
{
new WriteLine()
{
Text = new InArgument<string>("Type some word:")
},
new ReadLine()
{
BookmarkName = READ_LINE_BOOKMARK,
Result = new OutArgument<string>(response)
},
new WriteLine()
{
Text = new InArgument<string>((context) => "You've typed: " + response.Get(context))
}
}
};
}
That being said, please consider using IIS and AppFabric and you won't regret. AppFabric, with half a dozen clicks, takes care of two of the must painful things to implement within WF: persistence and monitoring. You won't never need to write the code bellow if you choose this path.
Deploy your workflow as a WCF application and you just call it as any other WCF contract. You've OperationContracts which are receive activities (those who wait and persist if it takes too long) and the corresponding send activities (those who return the value back to the client). You even have the concept of correlation among them. AppFabric takes care of the workflow resuming, just pass to it a previously initialized correlation handle.
AppFabric gives you a configuration UI to configure Persistence Store, monitoring and other options like how much time before goes idle and/or persist.
You can visualize Active/Idle/Suspended workflows, monitoring data, etc, etc.
Upvotes: 5