Monthly Archive for January, 2009

Exposing the State of a SharePoint Workflow Instance

Scenario

I have a fairly straight-forward sequential approval workflow that has an EnableModificationActivity that is in scope for just about the entirety of the workflow. My modification form is an ASPX page that gives the user the ability to enable/disable approval steps that have not occurred yet in the workflow. Since the workflow is able to be modified multiple times, I would like the form to reflect the current state of the workflow, meaning it should show which activities are currently enabled or disabled.

Solution

So here is the approach I ended up taking…

  1. I created a Plain Old CLR Object (POCO) class specific for holding state information my particular workflow that is capable of being XML Serialized. I’ll call this the “State Object” going forward.
  2. I created a reusable class called “WorkflowStateManager” that is capable of loading and saving single State Objects for a given SPWorkflow. This class is accessible by both the workflow and the modification form.
    • Save State Implementation:
      1. XML serializes the object to a string
      2. Sets the serialized string on the SPWorkflow list item’s property bag and calls the property bag’s Update() method
    • Load State Implementation (Essentially the reverse of the Save State implementation)
      1. Gets the serialized string from the SPWorkflow list item’s property bag
      2. XML Deserialize the string into the State Object
  3. When the workflow is activated, I construct a new State Object, initialize various properties on it, and save it using the WorkflowStateManager.
  4. As the workflow progresses, I load and update the State Object as needed in the following manner:
    • Use the WorkflowStateManager to load the current State Object
    • Make workflow decisions based on the State Object’s values
    • Make desired changes to the State Object
    • Use the WorkflowStateManager to save the State Object
  5. Now, my modification form is also able to load, manipulate, and save the State Object using the WorkflowStateManager, and in turn expose the current state of the workflow to the user.

I hope this might be of benefit to someone.

SharePoint Dependency Injection Approach

I just finished delivering a SharePoint workflow application in which I developed what could be called a start to framework that I would like to reuse in my next project. The framework currently has many features such as:

  • An “Object-List Mapping” infrastructure complete with lazy initialization of objects and collections, converters, and caching
  • A list item event broker
  • Several handy web controls
  • Logging
  • State management
  • Plenty of Utility Helpers

Each major feature is defined with an interface, and there is a single context class scoped to an SPWeb that holds references to each feature provider. For this last application, and I imagine I will do this for most applications going forward, I extended the context class to hold additional references to application-specific business logic helpers and data access interfaces.

In order to accomplish dependency injection of the feature providers, there is a context factory interface that is responsible for constructing and initializing contexts. I implemented a static ”default” context factory that when called upon, it first looks in the SPWeb property bag to see if a predefined custom context factory type has been specified. If it finds one, it constructs a new instance of the custom factory and returns the context from the custom factory’s CreateContext method.

    public static class SPAppContextFactory
    {
        public const string CustomSPAppContextFactoryKey = "custom_context_factory_key";
 
        public static void RegisterSPAppContextFactory<T>(this SPWeb web) where T : ISPAppContextFactory
        {
            web.AllProperties[CustomSPAppContextFactoryKey] = typeof(T).AssemblyQualifiedName;
            web.Update();
        }
 
        public static void UnregisterSPAppContextFactory(this SPWeb web)
        {
            if (web.Properties.ContainsKey(CustomSPAppContextFactoryKey))
            {
                web.AllProperties[CustomSPAppContextFactoryKey] = null;
                web.Update();
            }
        }
 
        public static SPAppContext CreateSPAppContext(this SPWeb web)
        {
            // First check Web Properties if an alternate factory is specified
            if (web.AllProperties.ContainsKey(CustomSPAppContextFactoryKey))
            {
                string customFactoryTypeName = web.AllProperties[CustomSPAppContextFactoryKey];
                Type customFactoryType = Type.GetType(customFactoryTypeName, true);
                ISPAppContextFactory customFactory = (ISPAppContextFactory)Activator.CreateInstance(customFactoryType);
                return customFactory.CreateContext(web);
            }
 
            // Create the Default Context
            return new SPAppContext(web);
        }

You can now register your applications custom context factory in the FeatureActivated method of a feature receiver. You’ll need to include the namespace of your static factory class in order to make the extension methods available, and then simply call the RegisterSPAppContextFactory extension method .

        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            var web = (SPWeb)properties.Feature.Parent;
            web.RegisterSPAppContextFactory<MyCustomContextFactoryClass>();
        }
 
        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            var web = (SPWeb)properties.Feature.Parent;
            web.UnregisterSPAppContextFactory();
        }

The context factory now serves as a single place where all dependencies are wired up, and the framework is now able to instantiate custom contexts without being aware of any feature provider implementation.