Navigation

Categories
Show Navigation Next Topic  »

Using FlexSource

IMPORTANT NOTE # 1 This topic provides the core principles of FlexSource and code excerpts from ASP.NET 4.6 FlexSource demo project. Please refer to that project for implementation details.

IMPORTANT NOTE # 2 Due to limitations in the current version of Entity Framework the FlexSource technology is not supported in Data Filtering feature of Code Effects. Please contact us if you are going to support Code Effects in your own LINQ provider and need our help.

To build and evaluate business rules, Rule Engine uses reflection and annotations on a class that encapsulates data. Such a class is called Source Object. For this to work properly all properties, fields, methods, relations, and restrictions of the source object must be known at design time. There are times, however, when the type of the source object is either unknown, or too complex, or can change at run-time based on variety of factors and reasons. Often, you don't even have a type to use as a source object but rather a couple of tables in a database, one for field names and their types, the other for their values.

FlexSource is designed to allow you to use Code Effects without having a "concrete" source type. It is a little more involved than using a plain .NET class or Source XML but the end result is well worth the effort.

FlexSource is split into two parts - the rule management (managing rules using Rule Editor) and the rule evaluation.

Rule Management

To create, edit, save or delete your business rules with Rule Editor using FlexSource you need to have a source object that extends the System.Type. You also need to extend certain abstract members of the following .NET types:

  • System.Type (as already mentioned)
  • System.Reflection.PropertyInfo
  • System.Reflection.ParameterInfo
  • System.Reflection.MethodInfo
  • System.Reflection.FiledInfo

That gives the Rule Editor ability to request a list of all properties, fields and methods that you want to use in your business rules at run-time. It's looks complicated but in reality it's a simple thing to do.

The above mentioned demo project uses a simple XML document /Models/FlexTypeData.xml that defines the structure of the source object and its data. The XML format has been chosen in order to simplify the demo. In real-life scenario it is likely that you store your fields and their values in a database. But the core principals remain the same.

Demo project declares a source object /Models/FlexType.cs that inherits from System.Type and implements Type's 7 properties and 6 methods:

public override string Name;
public override string FullName;
public override string AssemblyQualifiedName;
public override Type BaseType;
public override Module Module;
public override Type UnderlyingSystemType;
public override Assembly Assembly;

public override object[] GetCustomAttributes(bool inherit);
public override FieldInfo[] GetFields(BindingFlags bindingAttr);
public override Type[] GetInterfaces();
public override MethodInfo[] GetMethods(BindingFlags bindingAttr);
public override PropertyInfo[] GetProperties(BindingFlags bindingAttr);
protected override TypeAttributes GetAttributeFlagsImpl();

The code used by the demo to implement most of System.Type members is very basic; it's likely that your implementation is going to be the same or very similar. Only the listed members need to be implemented; the majority of the Type can be omitted because those members are not being invoked by Rule Editor when it reflects your source.

Obviously, your implementation of GetMethods() method should return all in-rule methods and rule actions that you want to be available for your business rules. The same goes for GetProperties() method, and so on. It is entirely up to your code where and how to get those lists of properties, fields and methods.

Notice that implementation of GetMethods() in demo project returns a list of FlexMethodInfo instead of the plain Reflection.MethodInfo type:

public override MethodInfo[] GetMethods(BindingFlags bindingAttr)
{
	List<FlexMethodInfo> methods = new List<FlexMethodInfo>();

	if (typeName == "Product")
	{
		methods.Add(new FlexMethodInfo("Concatenate", typeName));
		methods.Add(new FlexMethFlexMethodInfo("Register", typeName));
		return methods.ToArray();
	}

	return new MethodInfo[0];
}

We could have used .NET "info" classes, of course. But then you would have to needlessly implement many of their members without gaining anything in order for .NET reflection to work properly when Rule Editor reflects your source type. The demo project declares FlexPropertyInfo, FlexMethodInfo, FlexParameterInfo and FlexFieldInfo types in /Models folder. Use those classes as templates for your own implementation.

Rule Evaluation

To evaluate a rule you need a class that implements IFlexDataProvider interface. This class acts as run-time provider of your data. The role of IFlexDataProvider interface is to request fields, properties, and methods referenced in a business rule when that rule is evaluated.

You can definitely combine both "management" and "evaluation" classes into a single type with the following syntax:

public class MyCombinedSourceType : System.Type, IFlexDataProvider { }

... but we just wanted to show you that having two separate classes is also possible.

One of the public constructors of the "plain" Evaluator class takes the IFlexDataProvider as one of its parameters. This is how you initiate the FlexSource rule evaluation.

Basically, when Evaluator receives a rule and an instance of IFlexDataProvider data class as the source object it asks that object "Hey, I need the string value of the field named FirstName referenced in this rule." by calling its GetProperty(...) method and the source is expected to return the requested value. It's the job of the implementation of the GetProperty(...) method to know where and how to get that value.

The IFlexDataProvider declares five methods that your data provider class needs to implement:

using System;

namespace CodeEffects.Rule.Core
{
	public interface IFlexDataProvider
	{
		object GetProperty(object target, string name);
		object CallMethod(object target, string name, params object[] args);
		void SetProperty(object target, string name, object value);
		Type GetPropertyType(string propertyName);
		Type GetMethodReturnType(string methodName);
	}
}

When Evaluator receives the rule If Name starts with ABC then DoSomething it first calls your source object's GetPropertyType( "Name" ) and GetMethodReturnType( "DoSomething" ) methods to get type of the Name property and return type of the DoSomething action method and then it calls GetProperty(yourSourceInstance, "Name" ) to get the value of the Name property in order to have everything necessary to evaluate that rule.


Post your questions on Stackoverflow and become a part of our growing community

Comments: 0