R – How to avoid rebuilding the database everytime I make a change to the Windows Workflow

workflowworkflow-foundation

I am building a Windows Workflow into a .Net webapp. The problem I have is that every time I add or remove or change an activity in the state machine and then re-compile and run it, it no longer matches up to what is in the database for tracking and persistence. Instead of updating the changes, or creating a new version of the workflow in the database it throws an error. Sometimes it an "index out of bounds" error, other times its "Cannot get the member XXXXX."

To get it to work I have to delete the database and re-build it. I believe this works because when the workflow engine looks for the workflow in the database it doesn't find it and thus adds it to the database. This is very frustrating at the stage I am at, where I am building the workflow and it changes many times in a day.

Is there a way I can simply get the workflow engine to update the relevant tables in the database with the changes I have made to the workflow? Or overwrite it with a new version? I have looked at various webpages on setting up the workflow for dynamic updating at runtime but none of these suggestions worked for me. And whilst that is a feature I would like for my workflow, I am not sure if it is really the way to solve the problem I am having. It is not so much that I am creating a new version of the workflow, rather I am building a workflow and want to test the part I have just finished/added and I don't care about previous versions of the workflow and any partially complete workflows in the database.

Thanks.

Best Answer

I too faced similar problem , everytime Workflow design was change, all the old workflow instances in the DB use to throw error.

To overcome this , I created Tracking profile for each type of workflow. If I changed any workflow, i updated the version of my profile. This worked for me ..Here is the sample code -

For this you need to add tracking service to workflow runtime .

TrackingProfile profile = CreateProfile();

StoreProfileToDB(profile, connString, typeof(ExceptionWF.ParameterExceptionWF),"2.0.0.0");

private static TrackingProfile CreateProfile() { TrackingProfile myProfile = new TrackingProfile();

        ActivityTrackingLocation stateActivityLocation = CreateActivityLocation(typeof(StateActivity));
        AddActivityExecutionStatus(stateActivityLocation);

        ActivityTrackingLocation eventDrivenActLoc = CreateActivityLocation(typeof(EventDrivenActivity));
        AddActivityExecutionStatus(eventDrivenActLoc);

        ActivityTrackPoint actPt = new ActivityTrackPoint();

        actPt.MatchingLocations.Add(stateActivityLocation);
        actPt.MatchingLocations.Add(eventDrivenActLoc);
        myProfile.ActivityTrackPoints.Add(actPt);

        WorkflowTrackPoint workflowTrack = CreateWorkflowTrackPoint();
        myProfile.WorkflowTrackPoints.Add(workflowTrack);

        UserTrackPoint utp = new UserTrackPoint();
        UserTrackingLocation ul = new UserTrackingLocation();
        ul.ActivityType = typeof(HandleExternalEventActivity);
        ul.ArgumentType = typeof(object);
        ul.MatchDerivedArgumentTypes = true;
        ul.MatchDerivedActivityTypes = true;
        utp.MatchingLocations.Add(ul);

        myProfile.UserTrackPoints.Add(utp);
        myProfile.Version = new Version("1.0.0.0");
        return myProfile;
    }

    private static void StoreProfileToDB(TrackingProfile profile, string connString, Type wfType,string version)
    {
        TrackingProfileSerializer serializer = new TrackingProfileSerializer();
        System.IO.StringWriter writer = new System.IO.StringWriter(new StringBuilder());
        serializer.Serialize(writer, profile);
        SqlConnection conn = null;
        try
        {
            if (!String.IsNullOrEmpty(connString))
            {
                conn = new SqlConnection(connString);


                string storedProc = "dbo.UpdateTrackingProfile";
                SqlCommand cmd = new SqlCommand(storedProc, conn);
                cmd.CommandType = System.Data.CommandType.StoredProcedure;

                SqlParameter param = new SqlParameter("@TypeFullName", SqlDbType.NVarChar, 128);
                param.Direction = ParameterDirection.Input;
                param.Value = wfType.FullName;
                cmd.Parameters.Add(param);


                param = new SqlParameter("@AssemblyFullName", SqlDbType.NVarChar, 256);
                param.Direction = ParameterDirection.Input;
                param.Value = wfType.Assembly.FullName;
                cmd.Parameters.Add(param);


                param = new SqlParameter("@Version", SqlDbType.VarChar, 32);
                param.Direction = ParameterDirection.Input;
                //Note that you should increment version number for your
                //TrackingProfile to be able to use new TrackingProfile.
                //Default version is "1.0.0.0, It uses the default profile if not increamented.
                param.Value = version;
                cmd.Parameters.Add(param);

                param = new SqlParameter("@TrackingProfileXml", SqlDbType.NText);
                param.Direction = ParameterDirection.Input;
                param.Value = writer.ToString();
                cmd.Parameters.Add(param);

                conn.Open();
                cmd.ExecuteNonQuery();

            }//if
        }// try
        catch (Exception ex)
        {
            if (ex is SqlException)
            {
                //Check to see if it's a version error
                if (ex.Message.Substring(0, 24) == "A version already exists")
                {
                    EventLogger.Log("A profile with the same version already exists in database");
                }//if
                else
                {
                    EventLogger.Log("Error writing profile to database : " + ex.ToString());
                }
            }
            else
            {
                EventLogger.Log("Error writing profile to database : " + ex.ToString());
            }
        }
        finally
        {
            if (conn != null) { conn.Close(); }
        }
    }