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.