locked
Persitsence Problem with OnResolveBookmark RRS feed

  • Question

  • Hi all,

    I'm trying to Host a NonService Workflow with WorklfowServiceHost. In the Workflow are many Bookmarks that i want to resume. So i resume them with a WorkflowHostingEndpoint an override the OnResolveBookmark Method. The workflows shold be persited in an SQL Database.

    So when i Run the Workflow the Instance is persited in 4 Seconds with WorkflowIdleBehavior which I add to the WorkflowServiceHost. But when i Resume a Bookmark and the Workflow comes to the next Activity, the Workflow is not persisted in the DataBase. I have also added a FileTrackingParticipant, which said the same.

    Thats happenig after the first Bookmark:

    Idle...Persisted...Unloaded

    When i REsume the Bookmark:

    Resumed...Idle

    But why it doesnt Persist and unloads? The Instance is at the Second Bookmark but it isnt Persisted. When the Server Crachs the First ResumeBookmark is lost while the Instance is a long time Idle.

    Any Ideas?

    Thanks daniel

     

     

    Tuesday, March 8, 2011 12:41 PM

All replies

  • Hi,

    ->"But why it doesnt Persist and unloads? The Instance is at the Second Bookmark but it isnt Persisted. When the Server Crachs the First ResumeBookmark is lost while the Instance is a long time Idle."

    Could you share us a block of sample code.

    Regards
    MSDN Community Support
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    This posting is provided "AS IS" with no warranties, and confers no rights. My Blog: http://xhinker.com
    Microsoft Windows Workflow Foundation 4.0 Cookbook
    Monday, March 14, 2011 3:00 AM
  • Hi,

    here is my Code.

    I have an Interface:

    //ServiceContract exposed on the endpoint

    [

     

    ServiceContract(Name = "IWorkflowCreation")]

     

     

    public interface IWorkflowCreation

    {

    [

     

    OperationContract(Name = "Create")]

     

     

    Guid Create(IDictionary<string, object> inputs);

    [

     

    OperationContract(Name = "CreateWithInstanceId", IsOneWay = true)]

     

     

    void CreateWithInstanceId(Guid instanceId, IDictionary<string, object> inputs);

    [

     

    OperationContract(Name = "ResumeBookmark", IsOneWay = true)]

     

     

    void ResumeBookmark(Guid instanceId, string bookmarkName, object UserData);

    }

    And a Class which implements the WorkflowHostingEndpoint

     

     

    public class ResumeBookmarkEndpointKannWeg : WorkflowHostingEndpoint

    {

     

     

    public ResumeBookmarkEndpointKannWeg(Binding binding, EndpointAddress address)

    :

     

    base(typeof(IWorkflowCreation), binding, address)

    {

    }

     

     

    protected override Guid OnGetInstanceId(object[] inputs, OperationContext operationContext)

    {

     

     

    //Create called

     

     

    if (operationContext.IncomingMessageHeaders.Action.EndsWith("Create"))

    {

     

     

    return Guid.Empty;

    }

     

     

    //CreateWithInstanceId or ResumeBookmark called. InstanceId is specified by client

     

     

    else if (operationContext.IncomingMessageHeaders.Action.EndsWith("CreateWithInstanceId") ||

    operationContext.IncomingMessageHeaders.Action.EndsWith(

     

    "ResumeBookmark"))

    {

     

     

    return (Guid)inputs[0];

    }

     

     

    else

    {

     

     

    throw new InvalidOperationException("Invalid Action: " + operationContext.IncomingMessageHeaders.Action);

    }

    }

     

     

     

    protected override WorkflowCreationContext OnGetCreationContext(object[] inputs, OperationContext operationContext, Guid instanceId, WorkflowHostingResponseContext responseContext)

    {

     

     

    WorkflowCreationContext creationContext = new WorkflowCreationContext();

     

     

    if (operationContext.IncomingMessageHeaders.Action.EndsWith("Create"))

    {

     

     

    Dictionary<string, object> arguments = (Dictionary<string, object>)inputs[0];

     

     

    if (arguments != null && arguments.Count > 0)

    {

     

     

    foreach (KeyValuePair<string, object> pair in arguments)

    {

     

     

    //arguments for the workflow

    creationContext.WorkflowArguments.Add(pair.Key, pair.Value);

    }

    }

     

     

    //reply to client with the InstanceId

    responseContext.SendResponse(instanceId,

     

    null);

    }

     

     

    else if (operationContext.IncomingMessageHeaders.Action.EndsWith("CreateWithInstanceId"))

    {

     

     

    Dictionary<string, object> arguments = (Dictionary<string, object>)inputs[0];

     

     

    if (arguments != null && arguments.Count > 0)

    {

     

     

    foreach (KeyValuePair<string, object> pair in arguments)

    {

     

     

    //arguments for the workflow

    creationContext.WorkflowArguments.Add(pair.Key, pair.Value);

    }

    }

    }

     

     

    else

    {

     

     

    throw new InvalidOperationException("Invalid Action: " + operationContext.IncomingMessageHeaders.Action);

    }

     

     

    return creationContext;

    }

     

     

    protected override System.Activities.Bookmark OnResolveBookmark(object[] inputs, OperationContext operationContext, WorkflowHostingResponseContext responseContext, out object value)

    {

     

     

    Bookmark bookmark = null;

    value =

     

    null;

     

     

    if (operationContext.IncomingMessageHeaders.Action.EndsWith("ResumeBookmark"))

    {

     

     

    //bookmark name supplied by client as input to IWorkflowCreation.ResumeBookmark

    bookmark =

     

    new Bookmark((string)inputs[1]);

     

     

    //value supplied by client as argument to IWorkflowCreation.ResumeBookmark

    value = inputs[2];

    }

     

     

    else

    {

     

     

    throw new NotImplementedException(operationContext.IncomingMessageHeaders.Action);

    }

     

     

    return bookmark;

    }

    }

    Monday, March 14, 2011 12:52 PM
  • The server instantiates the following Class.

    public

     

     

    class LongRunningWorkflowService

    {

     

     

    Activity workflow = null;

     

     

    String WorkflowFile = string.Empty;

     

     

    static XName wfHostTypeName;

     

     

    private static readonly XName WorkflowHostTypePropertyName =

     

     

    XNamespace.Get("urn:schemas-microsoft-com:System.Activities/4.0/properties").GetName("WorkflowHostType");

     

     

    ResumeBookmarkEndpoint endpoint;

     

     

    Guid WFID;

     

     

    WorkflowServiceHost host;

     

     

     

    public LongRunningWorkflowService(Activity workflow_)

    {

     

     

    this.workflow = workflow_;

     

     

    //new WorkflowServiceHost(

    }

     

     

    public LongRunningWorkflowService(String workflowFile_)

    {

     

     

    this.WorkflowFile = workflowFile_;

    }

     

     

    public void StartHost()

    {

    wfHostTypeName =

     

    XName.Get("PyServiceWorkflowHost");

    host = GetHost();

     

     

     

    //Persistenz und idle

    host.Description.Behaviors.Add(SetupSqlWorklfowInstanceStoreBehavior());

     

     

    WorkflowIdleBehavior idleBehavior = new WorkflowIdleBehavior();

    idleBehavior.TimeToUnload =

     

    TimeSpan.FromSeconds(4);

    idleBehavior.TimeToPersist =

     

    TimeSpan.FromSeconds(2);

    host.Description.Behaviors.Add(idleBehavior);

     

     

    //Bookmark resume

     

     

    //endpoint = new ResumeBookmarkEndpoint(new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), new EndpointAddress("net.pipe://localhost/workflowCreationEndpoint"));

    endpoint =

     

    new ResumeBookmarkEndpoint(new BasicHttpBinding(), new EndpointAddress("http://localhost:8081/workflowCreationEndpoint"));

    host.AddServiceEndpoint(endpoint);

    host.WorkflowExtensions.Add(

     

    new FileTrackingParticipant());

     

    host.Open();

     

     

     

    foreach (var ep in host.Description.Endpoints)

    {

     

     

    Console.WriteLine("Contract: {0} at {1} binding: {2}", ep.Contract.Name, ep.Address, ep.Binding.ToString());

    }

    }

     

     

    public void StopHost()

    {

     

     

    if (host.State == CommunicationState.Opened)

    {

    host.Close();

    }

     

     

    else

    {

    }

    }

     

     

    private WorkflowServiceHost GetHost()

    {

     

     

    if (WorkflowFile == string.Empty)

    {

     

     

    return new WorkflowServiceHost(workflow);

    }

     

     

    else

    {

     

     

    String FullPathFile = Path.Combine(@"..\..\..\", this.WorkflowFile);

     

     

    WorkflowService service = XamlServices.Load(FullPathFile) as WorkflowService;

     

     

    if (service != null)

    {

     

     

    return new WorkflowServiceHost(service);

    }

     

     

    else

    {

     

     

    throw new NullReferenceException("Konnte die XAMLX Datei nicht in Host Starten");

    }

    }

    }

     

     

    private SqlWorkflowInstanceStoreBehavior SetupSqlWorklfowInstanceStoreBehavior()

    {

     

     

    string connectionString = ConfigurationManager.ConnectionStrings["WF Instance Store"].ConnectionString;

     

     

    SqlWorkflowInstanceStoreBehavior sqlWFInstanceStoreBhv = new SqlWorkflowInstanceStoreBehavior(connectionString);

    sqlWFInstanceStoreBhv.InstanceCompletionAction =

     

    InstanceCompletionAction.DeleteNothing;

    sqlWFInstanceStoreBhv.InstanceEncodingOption =

     

    InstanceEncodingOption.None;

    sqlWFInstanceStoreBhv.InstanceLockedExceptionAction =

     

    InstanceLockedExceptionAction.BasicRetry;

    sqlWFInstanceStoreBhv.HostLockRenewalPeriod =

     

    TimeSpan.FromMinutes(1);

     

     

    return sqlWFInstanceStoreBhv;

    }

    }

     

    So the Server does:

    LongRunningWorkflowService

     

     

    lrws = new LongRunningWorkflowService(new GeschäftsfeldAnalyse2());

    lrws.StartHost();

     

     

    Console.ReadKey();

    lrws.StopHost();

    The Client does:

    1. create an instance:

    ResumeBookmarkEndpoint

     

     

    endpoint = new ResumeBookmarkEndpoint(new BasicHttpBinding(), new EndpointAddress("http://localhost:8081/workflowCreationEndpoint"));

     

     

    IWorkflowCreation client = new ChannelFactory<IWorkflowCreation>(endpoint.Binding, endpoint.Address).CreateChannel();

     

     

    Guid WFID = client.Create(null);

    2. Resume The Bookmark

    ResumeBookmarkEndpoint

     

     

    endpoint = new ResumeBookmarkEndpoint(new BasicHttpBinding(), new EndpointAddress("http://localhost:8081/workflowCreationEndpoint"));

     

     

    IWorkflowCreation client = new ChannelFactory<IWorkflowCreation>(endpoint.Binding, endpoint.Address).CreateChannel();

    client.ResumeBookmark(WFid, FormId.ToString(), UserData);

     

     

    So thats it.

    I hope you will find the mistake.

    Monday, March 14, 2011 12:52 PM
  • Hi,

    I am not sure if I have fully understanding of the code partly due to the format.

    I found the following code in the StartHost method:
            //Persistenz und idle
            host.Description.Behaviors.Add(SetupSqlWorklfowInstanceStoreBehavior());
            WorkflowIdleBehavior idleBehavior = new WorkflowIdleBehavior();
            idleBehavior.TimeToUnload = TimeSpan.FromSeconds(4);
            idleBehavior.TimeToPersist = TimeSpan.FromSeconds(2);
            host.Description.Behaviors.Add(idleBehavior);

    Please also add these block code to the method which resume the persisted workflow.

    Note that when the workflow idle and unloaded, the WorkflowApplication instance will be GCed(remove from memory). so, when we resume workflow, we need to configure persistence and tracking again.

    Hope this helps
    Regards


    MSDN Community Support
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    This posting is provided "AS IS" with no warranties, and confers no rights. My Blog: http://xhinker.com
    Microsoft Windows Workflow Foundation 4.0 Cookbook
    Tuesday, March 15, 2011 2:24 AM
  • Hi,

    and thanks for your reply.

    I'm trying to Host a NonService Workflow with WorklfowServiceHost not with the WorkflowApplication.

    So i add a WorkflowHostingEndpoint which has a Method to resumes the Bookmark.

    I have one instance of WorkflowServiceHost. And this one has the WorkflowIdleBehavior().

    Sorry for the bad format of the above Code.

    Regards

    Daniel

    Wednesday, March 16, 2011 12:04 PM
  • Hi, Daniel

    ->"So i add a WorkflowHostingEndpoint which has a Method to resumes the Bookmark.

    I have one instance of WorkflowServiceHost. And this one has the WorkflowIdleBehavior()."

    Actually, WorkflowServiceHost is a wrapper of WorkflowApplication. If you want to build a NonService workflow, I would recommand you use WorkflowApplication directly. If you want to use Workflow Service, it is highly recommanded to host workflow service in IIS/Appfabric.

    You can also use your own code workflowServiceHost in IIS/Appfabric, See
    http://xhinker.com/post/WF4Create-Your-Own-ServiceHostFactory.aspx

    Regards


    MSDN Community Support
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    This posting is provided "AS IS" with no warranties, and confers no rights. My Blog: http://xhinker.com
    Microsoft Windows Workflow Foundation 4.0 Cookbook
    Thursday, March 17, 2011 3:08 AM
  • Did you ever figure out this problem?

    I'm having the exact same issue -- my WorkflowServiceHost-hosted workflows will persist when they go idle the first time, but after a resumption from a bookmark, they'll go idle but won't persist at that point.

    In broad strokes my code looks just like that -- which is probably not a coincidence, as both yours and mine looks essentially like the ResumeBookmarkEndpoint example, with the addition of the SQL persistence storage.

    Unfortunately Andrew Zhu's suggestion of using Workflow Application isn't an option in my case -- my original workflow hosting was using it, and persistence and resuming of workflows works like a charm, but durable timers don't work and there's no freakin' documentation on how to properly use WorkflowApplication to resume a durable timer, so I've been trying to rewrite the hosting layer using WorkflowServiceHost and have run into this problem, which is far more severe.

    Andrew's suggestion of reconfiguring the host on resumption doesn't make sense, and I can see the host is still configured properly in the debugger. If WorkflowServiceHost's creation of the underlying WorkflowService is broken, that I can't tell, but I assume WF 4.5 "works" correctly in that regard.

    Wednesday, June 26, 2013 12:58 PM
  • In case anyone else runs into this, this is the fix:

    http://social.msdn.microsoft.com/Forums/vstudio/en-US/a7724404-c254-4e5f-ba82-4b98c8c047e7/workflow-remains-idle-when-second-bookmark-is-created

    Basically, its a bug in the example code. In OnResolveBookmark, you need to either throw an exception or send a null response, or the workflow engine gets wedged in a state where the workflow won't persist.

    Just need to add:

    responseContext.SendResponse(null, null); 

    after you've set the value to return for the bookmark.

    • Proposed as answer by George Hartz Wednesday, June 26, 2013 1:56 PM
    Wednesday, June 26, 2013 1:56 PM