Using ASP.NET Dynamic Data with the Windows Workflow Foundation Rules Engine
In this post, I will show how to use the Windows Workflow Foundation rules engine to provide business logic for a Dynamic Data Entites Web Application. We will show how to change business rules without modifying code, drive the application based on a logical entity model, and map the entity model to a data store.
I have to admit that I am in awe of how easy this was to do.
Create the Dynamic Data Entities Web Application
To start, open Visual Studio 2008 with SP1 installed and create a new Dynamic Data Entities Web Application. This will generate what seems at first to be a lot of code, but once you peek under the hood you will see that it's really just templating code that you have full control over. I'm not going to explain all of Dynamic Data here, for more information you should check out the great getting started video series available at www.asp.net/dynamicdata.
Once you create the web application, add a new ADO.NET Entity Data Model. I named mine "Northwind.edmx". When propmpted, click OK to add the asset to the App_code folder. Next, a wizard pops up asking you what the model should contain. I chose "Generate from database" and used the Northwind database, choosing the Customers, Orders, and Order_Details tables.
Once you have the model created, you need to wire it up to the application. Before we do this, make sure you download the Dynamic Data Entity Framework Workaround. In your ASP.NET web application, right-click and choose "Add ASP.NET Folder" and choose "Bin". Then copy the workaround DLL into your Bin directory. We use this DLL to wire up the model to our dynamic data application. Open Global.asax and find the commented line starting with model.RegisterContext and change it to:
The other change I made was to update the route. Instead of using Edit.aspx for edits, I instead wanted to use ListDetails.aspx for a master/detail view, also allowing me to use the GridView control. I edited the Global.asax to the following.
You should be able to hit F5 and have a working application so far (make sure to set Default.aspx as the startup page for the web application).
Add Partial Types for Entities
There's probably a more elegant way to do this, but I needed a way to signal if an entity is valid and also to trap the validation message for the entity. The easiest way to do this is to create a partial type for your entity class and add 2 properties, Valid and ValidationMessage. We'll use these properties from our rules engine.
In hindsight, I probably could've created a common interface and used it for all of the entities. There might even be something there for Entity Framework and Dynamic Data to automatically add this type of error in, I'll leave investigation of this approach as an exercise to the reader.
Publish the Web Application
The next step is to deploy the web application. Right-click on the web project and choose "Publish Web Site". In that screen, I checked "Emit debug information" because I wanted to make sure that I could step through the types during debugging. Make sure to note the directory where you published the web site to, you will need this directory in a subsequent step.
Create the Rules Database
The next step is to download the External Ruleset Demo from the MSDN RuleSet Sample in the SDK. This is a fantastic demo application for Windows Workflow Foundation that allows you to use the Windows Workflow Foundation rules engine in your application without using workflows. Download the package and then run Setup.bat to create the Rules database. Next, load up the ExternalRuleSetToolKit.sln into Visual Studio 2008 to convert the application from Visual Studio 2005 to Visual Studio 2008 format. Once that's done, hit F5 to run the solution.
Create Rules for Your Application
Once the RuleSetTool application is running, click the New button. This will create a new ruleset. Give it a name (I called mine "ValidateCustomer"). On the top right of the form, there is a button to browse to a selected workflow or type. Click that browse button, then click browse again on the resulting "Workflow/Type Selection" dialog, and browse to the location of your published web application. Under that folder, choose "App_Code.dll".
After selecting the App_Code.dll, the dialog will show you a list of contained types and their members. I chose the "NorthwindModel.Customers" entity type that was generated by the Entity Framework designer.
Once you select the type and click OK, the final screen looks like this.
The next thing to do is to add your rules. This was the part where I stepped back and said "whoa, I can't believe this is so easy". Click the "Edit Rules" button. This is where you will actually define rules. For instance, I added a rule "AddressIsValid" where I check to see if the Address is null or empty. If it is, then I set Valid to false and set the ValidationMessage property to "Address is missing." Similarly, I added a rule that checks the CompanyName to see if it contains a specific value and set Valid and ValidationMessage appropriately.
Once you define the rules, click OK. On the main form, go to the "Rule Store" menu item and click Save.
Adding Custom Business Logic with Entity Framework
This one took awhile for me to find because I am not very familiar with Entity Framework. A quick search yielded "How to: Execute Business Logic When Saving Changes". This is where we evaluate the rules in our application.
Create a partial class for the context type. Add a partial method OnContextCreated that provides a handler for the SavingChanges event. In this event handler, add the following code.
Note that this is where the Valid and ValidationMessage properties come into play. We use this to determine if the rules fail and subsequently throw an exception. This is how we signal to our application that there are business logic errors above the field validation that we get through dynamic data and entity framework. However, now we have a problem... we need to handle the error in the application.
Handling the Validation Error in the Dynamic Data Application
This took me awhile to figure out, but the answer was really easy. When in doubt, steal borrow someone else's code! While researching how to handle asynchronous postback errors, I stumbled upon this example (http://asp.net/AJAX/Documentation/Live/mref/M_System_Web_UI_ScriptManager_OnAsyncPostBackError_1_3c0f9ecf.aspx). I modified their example slightly, and ended up with the following code. Open the master page, Site.master, and modify it to include script to handle the error.
The modifications I made include getting rid of the "clear" button, changing the background color to yellow, and changing the error font to red. Now, when an error occurs, the background is altered to signal to the user that there was a problem. This also leaves the ASP.NET GridView in the edit mode. One problem, though... how do we clear the error? This stumped me for awhile, until I came across the obvious solution. Simply add an onclick handler to the GridView in ListDetails.aspx.cs.
The Final Application
This is what is so cool... the whole application took me hardly any time at all and now I have business rules externalized from a fully functional application. When I click on the Customers entity, I have a grid of customers. Click edit, and change one of the values to have the rules engine set the Valid property to false. This causes the background to turn yellow and the error to display with a red font.
What's also incredibly cool is the user experience. The row is left in edit mode, the background is yellow, the font is red. They just click anywhere in the GridView, and the background returns to white and the error message goes away.

Now, here's what blows me away. With the web app still running, run the RuleSetTookit project again and change the rule. For instance, I changed the IsCompanyValid rule to check that any customer with a country anywhere other than "United States" to also have a "Region" value. Update the same record again, choosing the country as something other than United States, and the application will show another error. This is because the rules are externalized from the application, they can be changed on the fly without modifying your code.
I'll admit that it took me about 2 hours to put this together in the middle of phone calls and having to research stuff. I just don't get to code as much these days and my skills are rusty. However, once I learned how to do everything, I can put the whole site together in about 6 minutes. I should do the whole thing as a screencast just to show how little effort is involved to do this.
For More Information
www.asp.net/dynamicdata
Dynamic Data Entity Framework Workaround
External RuleSet Demo
How to: Execute Business Logic When Saving Changes
System.Web.UI.ScriptManager.OnAsyncPostBackError