Ron Jacobs

Windows Workflow Foundation

How To Load WF4 Workflow Services from a Database with IIS/AppFabric

How To Load WF4 Workflow Services from a Database with IIS/AppFabric

Rate This
  • Comments 2

This morning I saw a message post on the .NET 4 Windows Workflow Foundation Forum titled Load XAMLX from database.  I’ve been asked this question many times.

How can I store my Workflow Service definitions (xamlx files) in a database with IIS and AppFabric?

Today I decided to create a sample to answer this question.  Lately I’ve been picking up ASP.NET MVC 3 so my sample code is written with it and EntityFramework 4.1 using a code first approach with SQL Server Compact Edition 4.

Download Windows Workflow Foundation (WF4) - Workflow Service Repository Example

AppFabric.tv - How To Build Workflow Services with a Database Repository

Step 1: Create a Virtual Path Provider

Implementing a VirtualPathProvider is fairly simple.  The thing you have to keep in mind is that it will be called whenever ASP.NET wants to resolve a file or directory anywhere on the website.  You will need a way to determine if you want to provide virtual content.  For my example I created a folder in the web site called XAML.  This folder is empty but I found that it has to be there or the WCF Activation code will throw an exception.

When I want to activate a Workflow Service that is stored in the database I use a URI that will point to this directory like this http://localhost:34372/xaml/Service1.xamlx

public class WorkflowVirtualPathProvider : VirtualPathProvider
{
    #region Public Methods
 
    public override bool FileExists(string virtualPath)
    {
        return IsPathVirtual(virtualPath)
                    ? GetWorkflowFile(virtualPath).Exists
                    : this.Previous.FileExists(virtualPath);
    }
 
    public override VirtualFile GetFile(string virtualPath)
    {
        return IsPathVirtual(virtualPath)
                    ? GetWorkflowFile(virtualPath)
                    : this.Previous.GetFile(virtualPath);
    }
 
    #endregion
 
    #region Methods
 
    private static WorkflowVirtualFile GetWorkflowFile(string path)
    {
        return new WorkflowVirtualFile(path);
    }
 
    // TODO (01.1) Create a folder that will be used for your virtual path provider
    // Note: System.ServiceModel.Activation code will throw an exception if there is not a real folder with this name
    private static bool IsPathVirtual(string virtualPath)
    {
        var checkPath = VirtualPathUtility.ToAppRelative(virtualPath);
        return checkPath.StartsWith("~/xaml", StringComparison.InvariantCultureIgnoreCase);
    }
 
    #endregion
}

Step 2: Create a VirtualFile class

The VirtualFile class has to load content from somewhere (in this case a database) and then return a stream to ASP.NET.  Performance is a concern so you should definitely make use of caching when doing this.

public class WorkflowVirtualFile : VirtualFile
    {
        #region Constants and Fields
 
        private Workflow workflow;
 
        #endregion
 
        #region Constructors and Destructors
 
        public WorkflowVirtualFile(string virtualPath)
            : base(virtualPath)
        {
            this.LoadWorkflow();
        }
 
        #endregion
 
        #region Properties
 
        public bool Exists
        {
            get { return this.workflow != null; }
        }
 
        #endregion
 
        #region Public Methods
 
        public void LoadWorkflow()
        {
            var id = Path.GetFileNameWithoutExtension(this.VirtualPath);
 
            if (string.IsNullOrWhiteSpace(id))
            {
                throw new InvalidOperationException(string.Format("Cannot find workflow definition for {0}", id));
            }
 
            // TODO (02.1) Check the Cache for workflow definition
 
            this.workflow = (Workflow)HostingEnvironment.Cache[id];
 
            if (this.workflow == null)
            {
                // TODO (02.2) Load it from the database
 
                // Note: I'm using EntityFramework 4.1 with a Code First approach
                var db = new WorkflowDBContext();
 
                this.workflow = db.Workflows.Find(id);
 
                if (this.workflow == null)
                {
                    throw new InvalidOperationException(string.Format("Cannot find workflow definition for {0}", id));
                }
 
                // TODO (02.3) Save it in the cache
                HostingEnvironment.Cache[id] = this.workflow;
            }
        }
 
        /// <summary>
        ///   When overridden in a derived class, returns a read-only stream to the virtual resource.
        /// </summary>
        /// <returns>
        ///   A read-only stream to the virtual file.
        /// </returns>
        public override Stream Open()
        {
            if (this.workflow == null)
            {
                throw new InvalidOperationException("Workflow definition is null");
            }
 
            // TODO (02.4) Return a stream with the workflow definition
            var stream = new MemoryStream(this.workflow.WorkflowDefinition.Length);
            var writer = new StreamWriter(stream);
            writer.Write(this.workflow.WorkflowDefinition);
            writer.Flush();
            stream.Seek(0, SeekOrigin.Begin);
 
            return stream;
        }
 
        #endregion
    }

Step 3: Register the Virtual Path Provider

Finally you register the provider and you will be on your way.

protected void Application_Start()
{
    Database.SetInitializer(new WorkflowInitializer());
    AreaRegistration.RegisterAllAreas();
 
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
 
    // TODO (03) Register the Virtual Path Provider
    HostingEnvironment.RegisterVirtualPathProvider(new WorkflowVirtualPathProvider());
}

Ron Jacobs
http://blogs.msdn.com/rjacobs
Twitter: @ronljacobs http://twitter.com/ronljacobs

  • Ron, this is great. Do you see this leading to storing different versions of a XAMLX in the database and using filters, similar to WCF routing, to get the correct version for each client?

  • Many Workflow customers are ISVs which allow their customers to create/modify workflow definitions.  Most of the time they want to store the Workflow definitions in a database effectively treating them like data.  With this approach it becomes easier to do this.

    We like to listen to our customers and if there is demand for product features to make this easier we will definitely consider them.

Page 1 of 1 (2 items)