I am getting this exception:
variable 'e' of type 'MyClass' referenced from scope '', but it is not defined
I want to create dynamic rules with nested property. When I create static rules it works fine, but not in dynamic mode. Also, I want to use expression for dynamic rules using these lines of code if possible:
PatternBuilder customerPattern = builder.LeftHandSide().Pattern(typeof(Customer), "customer");
Expression<Func<Customer, bool>> customerCondition = customer => customer.Name == "John Do";
customerPattern.Condition(customerCondition);
I tried the code below to create and execute rules dynamically, but I am getting an exception. Why?
class Program
{
static void Main(string[] args)
{
try
{
CustomRuleRepository repository = new CustomRuleRepository();
List<RuleEngineEntity> rules = new List<RuleEngineEntity>();
rules.Add(new RuleEngineEntity { FieldName = "Age", Name = "CustomerCheck", Value = 20 });
repository.LoadRules(rules);
//Compile rules
var factory = repository.Compile();
//Create a working session
var session = factory.CreateSession();
RuleEngineRequestModel ruleEngineRequestModel = new RuleEngineRequestModel { ruleList = rules, customerData = new Customer { Name = "A", Age = 24 } };
session.Insert(ruleEngineRequestModel);
var IspassedorNot = session.Fire();
}
catch (Exception e) {
Console.WriteLine(e.Message);
}
}
}
public class RuleEngineRequestModel
{
public List<RuleEngineEntity> ruleList { get; set; }
public Customer customerData { get; set; }
}
public class RuleEngineEntity
{
public string Name { get; set; }
public int Value { get; set; }
public string Operator { get; set; }
public string FieldName { get; set; }
}
public class Customer
{
public string Name { get; set; }
public int Age { get; set; }
}
public class CustomRuleRepository : IRuleRepository
{
private readonly IRuleSet _ruleSet = new RuleSet("customerRule");
public IEnumerable<IRuleSet> GetRuleSets()
{
return new[] {_ruleSet};
}
public void LoadRules(List<RuleEngineEntity> list)
{
_ruleSet.Add(
BuildRule(list)
);
}
public List<IRuleDefinition> BuildRule(List<RuleEngineEntity> list)
{
NRules.RuleModel.Builders.RuleBuilder builder = null;
List<IRuleDefinition> rulesList = new List<IRuleDefinition>();
builder = new NRules.RuleModel.Builders.RuleBuilder();
builder.Name("CustomerDetail");
ParameterExpression customerParameter = null;
LambdaExpression customerCondition = null;
PatternBuilder customerPattern = null;
try
{
var orGroup = builder.LeftHandSide().Group(GroupType.Or);
foreach (var item in list)
{
var andGroup = orGroup.Group(GroupType.And);
customerPattern = andGroup.Pattern(typeof(RuleEngineRequestModel), item.Name);
customerParameter = customerPattern.Declaration.ToParameterExpression();
customerCondition =
Expression.Lambda(
Expression.GreaterThan(CreateParameterExpression(typeof(RuleEngineRequestModel), "customerData", typeof(Customer), item.FieldName),
Expression.Constant(item.Value)), customerParameter);
customerPattern.Condition(customerCondition);
}
Expression<Action<IContext>> action =
(ctx) => Console.WriteLine("Action triggered");
builder.RightHandSide().Action(action);
rulesList.Add(builder.Build());
}
catch (Exception e)
{
}
return rulesList;
}
public Expression CreateParameterExpression(Type type, string propertyName, Type type2, string propertyName2)
{
ParameterExpression pe = Expression.Parameter(type, "e");
Expression left = Expression.Property(pe, type.GetProperty(propertyName));
return Expression.Property(left, type2.GetProperty(propertyName2));
}
}
You have a
CreateParameterExpressionmethod, that's supposed to create a property access expression. But in that method you are creating a parameter expression there with a name "e", which does not correspond to any pattern that you defined in your rule. This causes the "variable not found" exception.You simply need one MemberExpression to access the
customerDataproperty, and then another one to access the nested property, configured in theRuleEngineEntity. Here is an updatedBuildRulemethod that implements the necessary expression tree.