One MEF to rule them all (Part 2)

2009/10/10

tags:
no comments

this post is the second of short series which cover the task of creating simple Rule Engine using MEF technology (part 1).

the post code is available for download here

Prerequisite

this post assume basic understanding of the MEF technology (for MEF introduction read this post)

 

The Contracts

as any decoupled framework the Rule Engine rely on contract.

the role of the MEF technology is to composite the part that export (publish) the contract into the contract consumer (import).

it is a matching game where the consumer declare its needs and MEF try to satisfy those needs with the available exported parts.

 

How do we define the Contract?

traditionally we used to use interface and abstract class with contract.

MEF contract is more flexible and it include the following options:

  • Interface (the traditional way)
  • Type (it is not necessarily best practice but it legal)
  • Delegate (the contract can be define at the method level)
  • String (decoration)

 

The Rule Engine Contracts

The rule contract assume single input (generic type) and return Boolean indication for pass/fail.

The Rule Engine is using 2 contract strategies:

  1. Delegate level contract
  2. Interface level contract

 

Rule’s Interface contract

public interface IRule<TInput>
{
string Title { get; }
string Description { get; }
Predicate<TInput> RulePredicate { get; }
}

looking at the contract we can see that this contract has 2 different responsibilities:

  1. Title and Description properties is basically rule decoration that may use by tools like Rule Simulation (which we discuss latter on this series)
  2. RulePredicate is the actually rule action (this is where the pass/fail logic goes)

(we may separate this contract into IRule<TInput> and IRuleDecoration<TInput>:IRule<TInput> for better separation of concern).

 

Rule’s Delegate contract

the rule delegate is using the .NET Predicate<T>.

the predicate will get input and return pass/fail indication.

also this contract having the entire rule runtime functionality its lack the metadata for the tooling like Rule Simulation.

 

The needs of Metadata

modern software development is taking into account the entire software ecosystem including tools friendly aspects.

the ability to visual the simulation of specific input can be a life saver while using large and complex rules bank.

it will ease the debugging process, reduce production errors and users will have experience administrating the rules.

 

The motivation of using Delegate contract along with the Interface one

the down side of using Interface contract is encapsulation issue.

each of our interface contract must be declare on separate class (we can mitigate it by having grouping metadata).

by using Delegate contract we can encapsulate multiple rules that sharing the same logical context into single class.

 

How to attach Metadata to delegate?

MEF does support the notion of metadata, one way of decorating MEF parts with metadata is by using attribute technology (MEF doesn’t force us into the attribute model but it is very convenient).

Step 1: is to define the metadata contract (we will use strongly typed metadata)

 

public interface IRuleMetadata 
{
string Title { get; }
string Description { get; }
}

Step 2: define attribute decoration for our metadata definition

 

[MetadataAttribute] // Decorate this attribute as metadata
[AttributeUsage(AttributeTargets.Method)]
public class ExportRuleAttribute : ExportAttribute, IRuleMetadata
{
public ExportRuleAttribute (Type exportType): base (exportType) {}
public string Title { get; private set; }
public string Description { get; set; }
}

notice that the attribute inherit from ExportAttribute (which used to publish parts as exportable).

 

Step 3: exporting (publishing) method as a rule

 

[ExportRule (typeof(Predicate<DateTime>), Title="FirstMonthHalf", 
Description="Valid for the first half of the month")]
public static bool FirstMonthHalf (DateTime input)
{
return input.Day < 16;
}

this snippet decorate rule for DataTime input

 

Step 4: consuming the Rules

the following class is having 2 private properties: (MEF can target private method)

  • Importing the Interface contract
  • Importing the Delegate contact

and one public property that expose the union of both technique (hiding the actual implementation from the class consumer).

 

public class RulesExecuter<T>
{
[ImportMany (AllowRecomposition=true,
RequiredCreationPolicy=CreationPolicy.Shared)]
private IEnumerable<Lazy<Predicate<T>, IRuleMetadata>>
RuleMethods { get; set; }

[ImportMany (AllowRecomposition=true,
RequiredCreationPolicy=CreationPolicy.Shared)]
private IEnumerable<Lazy<IRule<T>>>
RuleInstances { get; set; }
    public IEnumerable<Rule<T>> Rules {...}
}

the following snippet is the implementation of the Rules property (Rule<T> is class that defined to present rule data)

 

var rulesMtd = from item in RuleMethods
               select new Rule<T>
               {
                   Invoke = item.Value,
                   Title = item.Metadata.Title,
                   Description = item.Metadata.Description
               };
var rulesIns = from item in RuleInstances
               select new Rule<T>
               {
                   Invoke = item.Value.RulePredicate,
                   Title = item.Value.Title,
                   Description = item.Value.Description
               };
var rules = rulesMtd.Union(rulesIns);
 
Summary
we saw 2 different techniques for publishing extensions:
  1. using traditional Interfaces
  2. using Delegate

we learn to use metadata in order of having better support for tooling.

 

the post code is available for download here
Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*