Welcome

You have reached the blog of Keith Elder. Thank you for visiting! Feel free to click the twitter icon to the right and follow me on twitter.

Deep Fried Bytes Episode #51: Exploring Workflow Foundation 4.0 with Matt Winkler

Posted by Keith Elder | Posted in Podcast, Workflow Foundation | Posted on 13-04-2010

1

 
http://deepfriedbytes.com/podcast/episode-51-exploring-workflow-foundation-4-0-with-matt-winkler/

 

 

In this episode we sat down with Matt Winkler, the Program Manager for the Workflow Foundation team at Microsoft, to discuss Workflow Foundation 4.0.  We learn from Matt how in this release the team revisited the core of WF to increase performance and productivity as well as provide the best experience for developers adopting WF.  We discuss a lot of the changes surrounding WF 4.0 to enable WF to continue to be a strong foundational component developers can leverage in their applications.

WCF and WF No Application Endpoints Error

Posted by Keith Elder | Posted in WCF, Workflow Foundation | Posted on 13-03-2008

4

One of the new additions for Windows Workflow in .Net 3.5 was the ability to host Workflows using Windows Communication Foundation.  Two new activities were added to help with this.  The ReceiveActivity and the SendActivity.  I was recently playing with this and ran into a configuration error so I thought I’d post this to help others. 

After I had configured my workflow with a ReceiveActivity I added a WCF IIS project to my solution.  I configured it the way I thought it would work but I kept getting this error.  This was suppose to be a simple “hello world” experiment with WCF and WF but a few minutes later I was still scratching my head.

Service ‘Service.YourService’ has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element.

My service file in my IIS project looked like this:

<%@ ServiceHost Factory="System.ServiceModel.Activation.WorkflowServiceHostFactory" Language="VB" Debug="true" Service="CHCWorkflow.Prescriptions" %>

And my WCF configuration was like this:

image

 

For some reason when I configured the WCF service and when I configured the Workflow I changed my naming convention.  Here was the fix. 

  1. Go into the properties of the workflow by right clicking the workflow and pressing properties in the menu.
  2. In the properties window expand the WorkflowServiceAttributes node
    image
  3. Make sure the ConfigurationName property matches the name in the WCF configuration.  Here is a screen shot of the WCF configuration utility side beside the property to show you were I goofed.
    image

Once I got the names in sync the service started working.  I’m sure someone will run into this so I hope this saves you the headache it caused me. 

Part 3: Leveraging Workflow Foundation – Understanding the Runtime

Posted by Keith Elder | Posted in Workflow Foundation | Posted on 05-11-2007

1

Workflow Foundation‘s “brain” is the runtime.  The runtime’s main function is to handle all workflow instances, load the appropriate services, and keep track of what needs to happen next.   Before jumping in with Workflow Foundation it is important to understand the pieces that make up the workflow runtime and where the extensibility points within the runtime are located.  In this part, Understanding the Runtime, we’ll look at the pieces that makeup the runtime and discuss various things developers will want to consider.  To get started let’s look at a simple example of starting the runtime and executing a workflow. 

            using (WorkflowRuntime runtime = new WorkflowRuntime())
            {
                AutoResetEvent waitHandle = new AutoResetEvent(false);
                WorkflowInstance instance = runtime.CreateWorkflow(typeof(WorkflowConsoleApplication3.Workflow1));
                instance.Start();
                waitHandle.WaitOne();
            }

As we can see from the example above hosting the workflow runtime is really simple in this context.  Essentially once our runtime is created we use it to create our workflow from a type and then start our workflow instance.  The truth of the matter is the runtime isn’t really doing anything other than loading our workflow.  In other words in any application that leverages Workflow Foundation there would be a lot more lines of code to setup the runtime.  Workflow runtimes need to take into account several other factors before hitting the production floor such as setting up additional services and events for monitoring.   There are three things in particular developers starting out with Workflow Foundation should consider. 

One – Don’t Re-instantiate the runtime for every workflow instance

The first thing I recommend to developers starting out with Workflow Foundation is to use a singleton pattern or a factory pattern to make sure there is only one running instance of the WorkflowRuntime.  The reason we want one instance is there is a lot of overhead setting up and tearing down the WorfklowRuntime.  To give you an idea of the time it takes to just setup the runtime I ran a quick benchmark on my dual core 2.0GHz notebook.  The average time in milliseconds was between 40-42.  While that may not seem like a lot of time remember the runtime didn’t have to read any settings from the app.config file and it wasn’t using any additional services nor events.  It isn’t unusual to see one-two hundred millisecond start up times once you start getting into a production scenario depending on the amount of services.  Other than startup time the other reason we want to do this is we do not want workflow runtimes competing against each other.

Two – Understand Services

The second thing we need to familiarize ourselves with are services.  Think of workflow services as the main features of the runtime and an extensibility point whereby new features can be added.  For example tracking, scheduling, persistence, and profiling are services that ship out of the box.  By default the runtime uses the DefaultWorkflowSchedulerService.  This service handles how workflow instances are scheduled and manages the threads that runs workflows asynchronously.  While this is great the runtime uses this by default it doesn’t work in all scenarios.  Specifically if you are hosting within IIS or need to run workflows synchronously.  Other services such as the SqlWorkflowPersistenceService and the SqlTrackingService are unique services that allow developers to leverage SQL Server to store persistence and tracking information.  SQL Server schema information for these services is provided with the .Net 3.0 framework. Schemas and stored procedures for persistence and tracking can be found in the following location:

c:\windows\Microsoft.Net\Framework\v3.0\Windows Workflow Foundation\SQL

For more information about the various services and their roles be sure to browse the following links that discuss the various services in detail on MSDN.  We’ll look at several of these services down the road in another part of this series. 

Three – Events

The third thing we need to consider are the runtime events.  For example, a question often asked by developers starting out with Workflow Foundation is “How do I get properties out of my workflow after it runs?”.  In other to do this we have to use one of the events created by the runtime called WorkflowCompleted.  This event is fired once a workflow is completed and will give us access to the WorkflowCompletedEventArgs that provides access to output parameters.  For example if we had a workflow that calculated the max loan amount a client qualifies for that code may look something like this:

workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) 
                { 
                    waitHandle.Set();
                    int maxLoanAmount = (int)e.OutputParameters["MaxLoanAmount"];
                    Console.WriteLine(maxLoanAmount.ToString());
                };

This may seem a little weird at first but it does make sense if you consider long running workflows that may take several minutes to run or even months.  Workflows are more complicated than calling a single method and this is why the output parameters are in the completed event because it isn’t until the workflow gets down that we have all of our calculations.   Just remember what was said earlier about the default scheduling service.  If you are running calculated workflows like the example above that calculates if a person qualifies for a loan amount using the built-in rules engine of workflow more than likely it is going to need to run synchronously and therefore the default scheduler would need to be replaced by the manual scheduler within the runtime.

Of course there are others to consider as well such as WorkflowTerminated.  Depending on how the runtime is hosted your workflow you may want to be alerted when the runtime starts or stops and when workflows are persisted.  For example if you are hosting workflows within a Windows Service that is expected to stay running wire up events that sent alerts when the workflow terminated or suspended workflows unexpectedly.

Other Considerations

Hosting the workflow runtime isn’t tricky and can be configured via application config (app.config or web.config) files or within code.  For web hosted applications and services the web.config file is the best place to store workflow runtime information since this allows the runtime to be altered without having to be redeployed.  For windows services and possibly Winforms you may chose to go the API route. 

Developers looking to leverage Workflow Foundation in larger scenarios are going to ultimately want to customize the runtime.  This could be changing how workflows are persisted and tracked to creating their own custom scheduler.  The nice thing is Workflow Foundation is ready to be customized. 

One idea of extensibility may be using the file system to store workflows instead of SQL Server.  For those looking for this option you can find an example already done on MSDN here.  Another extensibility point is scheduling.  There was an example we ran into at work whereby workflow runtimes using the manual scheduler in IIS could not call other workflows.  A custom scheduler did the trick.  You can read more about that problem and resolution here.

In part four of Leveraging Workflow Foundation we’ll look at hosting workflows and how this knowledge in applied in various scenarios.

Part 1: Leveraging Workflow Foundation – It is just an API
Part 2:  Leveraging Workflow Foundation – Invest in your Workflow Infrastructure

 

Technorati tags: ,

Cannot Invoke Workflows From Workflows Hosted in Asp.Net with Manual Scheduler

Posted by Keith Elder | Posted in Asp.Net, Web Services, Workflow Foundation | Posted on 30-07-2007

3

WasherAs you can see I’ve included my famous rusty washer on this post to denote a problem.  I was having a conversation with a team member about Workflow Foundation who was having a problem.  The problem was “Hey Keith, have you ever had workflows in Workflow Foundation invoke workflows?”.  Answer:  I  have not, but let’s talk about it, what’s the problem?

 My team member started explaining that they have a need for workflows to call other workflows.  Sounds simple enough since their is a pre-packaged activity in WF to do just this in the toolbox.

He further elaborated that when they invoke the workflows the workflow runtime dies.  At first I was perplexed, but then knowing they were hosting it in Asp.Net web service I asked which scheduler they were running.  He replied ManualWorkflowSchedulerService.  Ah ha!

The problem is a catch 22 and honestly I don’t have a solution for this.  The problem is that when hosting a workflow with the ManualWorkflowSchedulerService it cannot invoke other workflows.  I guess it makes sense since it cannot spawn a new thread, after all it is trying to process a new instance of a workflow on the same thread. (at least that is my theory) Is there a work around?  I don’t know.  Is it a problem, yeah!

To compound matters the only way to reliably host workflow foundation in Asp.Net service is with the ManualWorkflowSchedulerService.  Well, ok it isn’t the only way, but if there are problems using the default scheduler in Asp.Net.  Here is Paul Andrew’s post about a fix coming in WF where he explains the differences.

The DefaultWorkflowSchedulerService is the out-of-box Windows Workflow Foundation scheduler used by the runtime to execute workflow instances on new CPU threads.  These threads are leveraged from the.NET thread pool associated with the host application. This works well unless the workflow runtime is hosted in ASP.NET.  Because of the limited thread resource in the IIS server, we would be allocating an additional unnecessary thread to execute the workflow instance for every HTTP request that required workflow execution. The ManualWorkflowSchedulerService was developed to address this issue by allowing the thread that is processing the HTTP request to execute a workflow instance. Unfortunately in WF Beta 2.2 and prior, it didn’t handle the correct processing of timer events (i.e. delay activities).

When doing Asp.Net Web Services the only reliable way to return output parameters, catch exceptions, logging, handle faults, etc is to run the workflow on the same thread synchronously using the ManualWorkflowSchedulerService.  I’ve tried every which way I knew to get the default scheduler to work but couldn’t.  If you think about it, it makes sense since web services receive a message, then typically return a message.  With the default scheduler it is hard to bubble up exceptions properly so they can be logged or caught and handle gracefully in a service.

To test this theory I created a console workflow app and created two workflows with one calling the other like this.

image

I then wired up the ManualWorkflowSchedulerService in the Program.cs like this and run it.  It doesn’t run. If I comment out the manual scheduler and use the default, it works fine.

static void Main(string[] args)

        {

            try

            {

                using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())

                {

                    ManualWorkflowSchedulerService service = new ManualWorkflowSchedulerService(false);

                    workflowRuntime.AddService(service);

                    AutoResetEvent waitHandle = new AutoResetEvent(false);

                    workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) { waitHandle.Set(); };

                    workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)

                    {

                        Console.WriteLine(e.Exception.Message);

                        waitHandle.Set();

                    };

 

                    WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1));

                    ManualWorkflowSchedulerService scheduler = workflowRuntime.GetService<ManualWorkflowSchedulerService>();

                    instance.Start();

                    scheduler.RunWorkflow(instance.InstanceId);

                    waitHandle.WaitOne();

                }

            }

            catch (Exception ex)

            {

                Console.WriteLine(ex.Message);

                Console.ReadLine();

            }

 

 So the catch 22 is I need to use the manual scheduler with Asp.Net Web Services but I can’t invoke other workflows if I do.  If I use the default scheduler, I can’t scale my service.  The “solution” we are going with right now for the work around is to host the service in WCF as a Windows Service outside of IIS.    The bad part of this is you can’t really scale this either.  That’s the nice thing about using IIS is it really is an app server.  Clustering and those things are widely available.  If you build your own windows service you lose all of that baked in functionality.  I think this is a HUGE rusty washer.   Anyone faced this challenge?  How did you solve it?  Am I off my rocker on this one?  Feedback would be appreciated.

UPDATE (8.28.2007)

One of the developers at work (Joe Brach) solved this problem since he was running into the same issue.  I thought I’d post his example of his fix since several people have been emailing me if I found a fix.  The “fix” is you create your own class that extends WorkflowRuntimeService.  Here is Joe’s fix:

    public class StartWorkFlowService: WorkflowRuntimeService

    {

        public Guid StartWorkflow(Type workflowType, Dictionary<string, object> inparms

        {

            //Get an instance of the runtime

            WorkflowRuntime wr = this.Runtime;

 

            //Create the type of Workflow

            WorkflowInstance wi = wr.CreateWorkflow(workflowType, inparms);

 

            //Start the WorkFlow

            wi.Start();

 

            //Get the Manual Workflow Service

            ManualWorkflowSchedulerService ss = wr.GetService<ManualWorkflowSchedulerService>();

 

            if (ss != null)

            {

                ss.RunWorkflow(wi.InstanceId);

            }

            return wi.InstanceId;

        }

    }

Part 2: Leveraging Workflow Foundation – Invest in Your Workflow Infrastructure

Posted by Keith Elder | Posted in .Net, Web Services, Workflow Foundation | Posted on 28-05-2007

13

In Part 1 of Leveraging Workflow Foundation we discussed how Workflow Foundation is just an API.  Since APIs don’t provide business value out of the box, it is important engineers exploring Workflow Foundation for the first time approach it in the same manner they would any API: write once, run everywhere.  In other words, instead of building out the workflow components for one application, look at it from the 10,000 feet view.  This is of course going to take more time than one might originally think so be sure to allow for this in development time.  We’ll look at the basics engineers starting with Workflow Foundation will need to think about to invest in their workflow infrastructure.  Who knows, we may even find some rusty washers and shinny pennies along the way.

Think about workflow from a central service for your business not just within one application.  Think about workflow outside of the box.  Think about reusability.  Think about a central rules engine.

While this will not be a comprehensive guide on how to build out one’s infrastructure with Workflow Foundation, I do want to point out several entry points everyone should consider.  The idea is to invest time in Workflow Foundation just as one might in invest other areas of .Net like Asp.Net for example.  I use Asp.Net as an example because most engineers that build Asp.Net web sites have some type of reusable framework they can apply to other web sites they build.  If the same principal is applied to Workflow Foundation and done correctly, it could pay off huge dividends for a business.

The Big Picture From a 10,000 Feet View

We understand we need to invest in our workflow infrastructure.  What are the things we need to spend our time on to make Workflow Foundation more reusable across our teams?  Let’s first look at our goal.  Our goal is to build out a core workflow infrastructure that can be reused among various projects, teams, and applications.  

Now that our goal is set, here are the things we need to focus on to make this a reality:

  1. Provide a central location where workflows can be stored.
  2. Provide a central location where rules can be stored.
  3. Provide a central service where workflows can be invoked.
  4. Provide a central service and API where rulesets can be manipulated and invoked against workflows or instances of any object.
  5. Provide a custom user interface to easily create, modify, update, track and manage existing workflows and rules.  Also this should have the ability to deploy workflows and rules to different environments (test, beta, prod).

As we see there is A LOT that needs to be done to build out a reusable infrastructure.  Let’s start with the first two items above since these are relatively easy to accomplish.   The reason I used the words “easy to accomplish” is because workflows and rules associated with workflows can be expressed in XML.  Once we persist our workflow to XML, which is called XOML, it becomes trivial to store the XOML in a database.  Rules associated with workflows are already created in XML and this makes it easy to store these.  The harder one to deal with is the workflow though.  More on that later. 

When we create our database structures to store workflows and rules XML it is important we do not build our tables in a way where rules are always tied to a workflow.  In other words don’t build a single table.  Workflows and rules should be stored separately and by doing so gives us some nice reusability with the rules engine as we’ll see later on.  Remember we are trying to create not only a reusable workflow infrastructure but a reusable rules engine as well.  Now that we understand workflows and rules can be stored in a database let’s look at how this will be accomplished.

Storing XOML Workflows in the Database

As mentioned earlier we can express our workflows in XML.  At the time of this writing Visual Studio supports two types of workflows.  A code behind workflow and a XOML workflow.  It is the XOML workflow type that we are interested in.  Since the XOML workflow is essentially XML, we can easily store it into a database. 

To persist workflows we need to first create our workflows as XOML based workflows.  With a little bit of code we could persist the XOML into a database.  The next question is how do we instantiate workflows from XOML.   The answer is easy but it is a matter of knowing where to look.  To create an instance of a XOML workflow we have to dig a little deeper into the APIs but not too far.  The method we are going to look at is CreateWorkflow().  CreateWorkflow() has six overrides and it is the sixth override that provides us the entry point we need not only for workflows but also to pass in rules our workflow would use.  Here is the syntax of it:

public WorkflowInstance CreateWorkflow (

                XmlReader workflowDefinitionReader,

                XmlReader rulesReader,

                Dictionary<string,Object> namedArgumentValues

            )

 

The reason this override method in particular is interesting is it allows us to pass in all the arguments we need to kickoff our workflow including our XOML, our rules and any parameters the workflow needs. 

When you first add a workflow object to your solution, but sure to select the one that is XOML based, here is a screen shot from Visual Studio.

Assuming an interface is built to manipulate workflows outside of Visual Studio, using the XOML based workflow will allow us to adjust the workflows in real time without having to recompile assemblies and redeploy them (assuming the code behind doesn’t need modified).  Be sure to download the workflow samples available as it contains examples of how to rune workflows with XOML.   The main thing missing from Visual Studio though are tools to assist in running and deploying XOML based workflow definitions so plan on devoting a lot of time to this area as the documentation and tools available are slim.  Keep your eye on the prize though! 

Just for the record, here is a sample hello world XOML workflow to give you an idea what the XOML looks like:

<SequentialWorkflowActivity x:Class=WorkflowLibrary1.Workflow2 x:Name=Workflow2 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml xmlns=http://schemas.microsoft.com/winfx/2006/xaml/workflow>

    <CodeActivity x:Name=codeActivity1 ExecuteCode=codeActivity1_ExecuteCode />

    <CodeActivity x:Name=codeActivity2 ExecuteCode=codeActivity2_ExecuteCode />

</SequentialWorkflowActivity>

 

The first code activity prints “Hello” and the second prints “World”.  Since we’ve expressed this in XML we could easily manipulate the workflow to print “World” then “Hello” simply by moving the codeActivity2 above codeActivity1.  We could do this without needing to redeploy any code.

The hard part of this is tying the ability to persist XOML workflows with the big picture item number five.  Storing our XOML in a database doesn’t necessarily make us agile from a business standpoint, it is the interface the end users (business analyst or other engineers) interact with and the central workflow services we expose that is going to make us more agile.  This is where the majority of your time is going to be spent.  There is good news in that there are numerous samples floating around on the Internet to give you a better idea.  Be sure to checkout the samples here.  You are also going to have to devote a fair amount of time to the ComponentModel to create the desired UI experience to load a workflow from XOML, alter it, and then persist it back. 

As we discussed in part 1 there are rusty washers and this is one of them.  While we can off load workflows to the database we will find there aren’t many tools to assist us in doing so and the art of doing this will seem kludgy and an after thought at best.  I personally spent several hours trying to get workflows to reliably load from the file system and couldn’t.   I’m sure it was something simple I wasn’t doing but the whole process was very inelegant.  Hopefully the Workflow Team will beef up their tool offerings in the next version of Visual Studio to make this more trivial.  Because of the lack of time to devote to this our team decided we would revisit it at a later date when we had more time.

 

Storing Workflow Rules in the Database

We’ve looked at storing workflows so let’s move onto storing rules in the database.  The rules engine aspect of Workflow Foundation turned out to be a shiny penny and one that isn’t talked about much.  #2 and #4 of our big picture items is off loading rules to the database and then creating a central service where rules can be invoked. 

When we add a “policy” activity to a workflow we can associate one or more rules to it.  Once a rule is defined we can also re-use it within the workflow.  That’s great, but what if we want to reuse the same rules again in a different workflow?  This is one reason why we want to off load these rules into a separate location.  When rules are created within Visual Studio by default they are stored in a hidden file called .rules.   The nice thing about the rules is they are already written in XML. Here is a screen shot so you can see how this appears from Visual Studio.

 

This is also where the second parameter of CreateWorkflow() override number six comes into play.  We can easily offload the rules we’ve created into another database table or set of tables and then pass those into the workflow at runtime.  Not only can we leverage the built in rule APIs within Workflow Foundation for workflows we can also utilize this for validating instances of objects that could then be used across multiple applications.   During my exploration, once this light bulb went on I realized that I could really save my team a lot of time.  I started down the path of building out a tool that could not only serve the purpose of storing rules for workflows but also a tool that could be called via a service from any application.  I call the tool “Orbb”.

Orbb – Object Rules Business Builder

According to our goals as outlined above, we knew we wanted to store rules in a database and then expose functionality via a service.  To make ourselves more agile we need an interface to manipulate these rules.  During my exploration of the rules engine that ships with Workflow Foundation it became apparent that rules can be run against ANY INSTANCE OF ANY OBJECT.  This is powerful because it means we aren’t tied to XML like Biztalk for example.  For example we could have a Winform instance that needs to have rules performed against it, or maybe an Asp.Net web page, or may a simple business entity like a purchase order.  As long as it is an “instance” we can leverage the rules engine that ships with Workflow Foundation.

Once this light bulb went off, a few hours later I had a database, WCF service and a user interface to build, manage and centrally store rules for a variety of applications.  Orbb was then born.  Today Orbb is just a proof of concept but the value it could provide is unquestionable.   It is in a very infant stage right now and may never see the light of day but it is far enough along I can share a screen shot with you here.  Here is a screen shot:

The idea is fairly simple.  An application is created within Orbb and then permissions are setup (who can edit / change rules for each application) along with the various environments you need to test your rules (test, beta, prod).  Those that are granted access can then use Orbb to manage not just one application but multiple applications.  Imagine one central service where rules could be stored, validated and retrieved. 

For example, imagine you have a smart client where users can create a new helpdesk ticket.  The business informs you that users aren’t entering enough information in the “what is wrong” textbox and they want you to force them to enter at least 150 characters.  Instead of having to add this rule to your code by hand and re-push it, you could simply launch Orbb, select the form and add the new rule to the form.  Then copy the new ruleset to your test, beta and prod environments without having to redeploy your application. 

This type of flexibility could also be applied to workflows whereby rules change.  Being able to adjust your workflow rules without having to redeploy is definitely worth an investment.   One of the things that came out of Orbb as I worked through it was a generic class that takes any Object and retrieves the rules from a database and then validates the object using the rules.  You could utilize this in a service, Winforms, Asp.Net pages, business objects and more.  I have provided a copy of this below in the “Validating Any Object with Workflow Rules Engine” section.

Conclusion

As you can see there is quite a bit of work that can be done to leverage Workflow Foundation and your mileage will vary in regards to your time / benefit ratio.  In the end it will pay in huge dividends if an investment is done up front, not as an after thought.  In part 3 of Leveraging Workflow Foundation we’ll look at hosting the workflow runtime.  Will we find any rusty washers?  Stay tuned!

Validating Any Object With Workflow Rules Engine

using System;

using System.Collections.Generic;

using System.Text;

using Microsoft.Practices.EnterpriseLibrary.Data;

using System.Data.Common;

using System.IO;

using System.Xml;

using System.Workflow.Activities.Rules;

using System.Workflow.ComponentModel.Serialization;

using System.Workflow.ComponentModel.Compiler;

 

namespace Orbb.BusinesLayer

{

    /// <summary>

    /// This is a generic class that will take any object and process rules

    /// against that object that are defined in a database.

    /// </summary>

    public class ObjectValidation

    {

        private object entity;

 

        /// <summary>

        /// The object that needs to be validated.

        /// </summary>

        public object Entity

        {

            get { return entity; }

            set { entity = value; }

        }

 

        private string ruleSetName;

 

        /// <summary>

        /// Name of the RuleSet that is in the database we need

        /// to use.

        /// </summary>

        public string RuleSetName

        {

            get { return ruleSetName; }

            set { ruleSetName = value; }

        }

 

        private System.Workflow.Activities.Rules.RuleSet ruleSet;

 

        /// <summary>

        /// Deserialized RuleSet object which contains all the rules.

        /// </summary>

        public System.Workflow.Activities.Rules.RuleSet RuleSet

        {

            get { return ruleSet; }

            set { ruleSet = value; }

        }

 

        private RuleValidation ruleValidation;

 

        public RuleValidation RuleValidation

        {

            get { return ruleValidation; }

            set { ruleValidation = value; }

        }

 

 

        private WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();

        /// <summary>

        /// Provides the extensible Application Markup Language (XAML) serialization services

        /// to workflow at design time.

        /// </summary>

        public WorkflowMarkupSerializer Serializer

        {

            get { return serializer; }

            set { serializer = value; }

        }

 

        private System.Workflow.Activities.Rules.RuleExecution ruleExecution;

        /// <summary>

        /// Stores state information while executing RuleCondition or RuleAction classes.

        /// </summary>

        public System.Workflow.Activities.Rules.RuleExecution RuleExecution

        {

            get { return ruleExecution; }

            set { ruleExecution = value; }

        }

 

        public ObjectValidation(Object entity, string ruleSetName)

        {

            this.Entity = entity;

            this.RuleSetName = ruleSetName;

 

            // Get the rule from the database

            this.GetRuleFromDatabase();

        }

 

        /// <summary>

        /// Gets the rule from the database as specified.

        /// </summary>

        private void GetRuleFromDatabase()

        {

            Database db = DatabaseFactory.CreateDatabase(“OrbbConnectionString”);

            using (DbCommand cmd = db.GetSqlStringCommand(“select RuleSet from Rules where Name=@Name”))

            {

                db.AddInParameter(cmd, “Name”, System.Data.DbType.String, this.RuleSetName);

                string ruleSet = (string)db.ExecuteScalar(cmd);

                this.DeserializeRuleSet(ruleSet);

            }

            this.ValidateRuleSet();

        }

 

        /// <summary>

        /// Deserializes a ruleset from the database into a RuleSet object.

        /// </summary>

        /// <param name=”ruleSetXmlDefinition”></param>

        private void DeserializeRuleSet(string ruleSetXmlDefinition)

        {

            if (!String.IsNullOrEmpty(ruleSetXmlDefinition))

            {

                StringReader stringReader = new StringReader(ruleSetXmlDefinition);

                XmlTextReader reader = new XmlTextReader(stringReader);

                this.RuleSet = Serializer.Deserialize(reader) as RuleSet;

            }

        }

 

        /// <summary>

        /// Executes the rule set on the given entity.

        /// </summary>

        public bool ExecuteRule()

        {

            if (null != ruleExecution)

            {

                try

                {

                    ruleSet.Execute(ruleExecution);

                }

                catch

                {

                    throw;

                }

                return true;

            }

            else

            {

                return false;

            }

        }

 

        /// <summary>

        /// Validates that the entity being processed contains the properties

        /// defined in the ruleset.

        /// </summary>

        private void ValidateRuleSet()

        {

            ruleValidation = new RuleValidation(this.entity.GetType(), null);

            if ((null != ruleValidation) && (!ruleSet.Validate(ruleValidation)))

            {

                string errors = “”;

                foreach (ValidationError validationError in ruleValidation.Errors)

                {

                    errors = errors + validationError.ErrorText + “\n”;

                }

                throw new Exception(errors);

            }

            else

            {

                ruleExecution = new RuleExecution(ruleValidation, this.entity);

            }

        }

 

    }

}

 

 

Like this article? kick it on DotNetKicks.com