How to write a caching Policy Activity to improve the performance of your WF rules

To follow up on my post from yesterday on Tuning the performance of your WF Rules, here is some code that you can use to write a custom PolicyActivity that does caching of rule sets.

The dictionary caches your rule set.

public class PolicyCacheService : WorkflowRuntimeService

    {

        private Dictionary<Type, RuleDefinitions> ruleCachedDefinitions = new Dictionary<Type, RuleDefinitions>();

        public RuleDefinitions GetRuleDefinitions (Type workflowType)

        {

            RuleDefinitions ruleDefinitions = null;

            lock (ruleCachedDefinitions)

            {

                ruleCachedDefinitions.TryGetValue(workflowType, out ruleDefinitions);

                if (ruleDefinitions == null)

                {

                    ruleDefinitions = GetRuleDefinitionsFromManifest(workflowType);

                    ruleCachedDefinitions.Add(workflowType, ruleDefinitions);

                }

            }

            return (ruleDefinitions);

    }

    private RuleDefinitions GetRuleDefinitionsFromManifest(Type workflowType)

    {

          if (workflowType == null)

                throw new ArgumentNullException("workflowType");

            RuleDefinitions rules = null;

            String resourceName = workflowType.Name + ".rules";

            Stream stream = workflowType.Module.Assembly.GetManifestResourceStream(workflowType, resourceName);

            if (stream == null)

                stream = workflowType.Module.Assembly.GetManifestResourceStream(resourceName);

            if (stream != null)

  {

                using (StreamReader reader = new StreamReader(stream))

                {

                    using (XmlReader xmlReader = XmlReader.Create(reader))

                        rules = new WorkflowMarkupSerializer().Deserialize(xmlReader) as RuleDefinitions;

                }

            }

            return rules;

        }

    }

The execute method of your custom activity would look something like this.

Here, you access the rule set from the PolicyCacheService - the PolicyCacheService returns you a rule set from its cache or fetches it from disk.

       protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)

        {

            PolicyCacheService polCacheSvc = executionContext.GetService<PolicyCacheService>();

            if (polCacheSvc == null) throw new InvalidOperationException("PolicyCacheService is needed");

  Activity root = this;

            // Getting the root activity

            while (root.Parent!= null)

            {

                root = root.Parent;

            }

            for (int i = 0; i < RuleSetNames.Length; i++)

            {

                RuleDefinitions ruleDefs = polCacheSvc.GetRuleDefinitions(root.GetType());

                RuleSet rs = ruleDefs.RuleSets[RuleSetNames[i]];

                if (rs != null)

                {

                    RuleEngine re = new RuleEngine(rs,root.GetType());

                    re.Execute(root,executionContext);

                }

            }

            return base.Execute(executionContext);

        }