Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Cross Post from Ron Jacobs blog
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 https://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
https://blogs.msdn.com/rjacobs
Twitter: @ronljacobs https://twitter.com/ronljacobs
Comments
- Anonymous
August 11, 2011
Thanks for the great sample. I found that adding the GetCacheDependency override allows you to avoid having the xaml folder, but using sample code gets an error. The link forums.asp.net/.../1289756.aspx said returning null works, which it does. Do you have insight as to why that works?public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart){ if (IsPathVirtual(virtualPath)) return null; else return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);} - Anonymous
August 11, 2011
Thanks Jim! No I don't know why returning null works. - Anonymous
August 18, 2011
i am wondering how we would use appfabric with this type of sitiuation, in case we want to monitor or persist a service, since these services are not visible through IIS in Appfabric dashboard - Anonymous
August 26, 2011
@ashish The service would still emit tracking events in the monitoring store but as you point out AppFabric would not display it as a service or offer control operations on it. - Anonymous
September 01, 2011
To answer the question I posted, I found that a wsHttp service is created for the xamlx at http://<host>/xaml/<wfname>.xmalx/System.ServiceModel.Activities_IWorkflowInstanceManagement - Anonymous
November 24, 2011
Hi Ron... this is working like a charm.However, is there a way I can use the WorkflowServiceHostFactory while using this VirtualPathProvider?Any idea, please let me know.Thanks!