问题
I want to validate rule against input array data with runtime indexer not with some fixed zero index value. It works if i insert data one by one in session.Insert() It does't work if i use sesion.InsertAll() with array data
I tried providing Expression.Constant value to indexer but no action triggers
class Program
{
static void Main(string[] args)
{
RuleTestWithSingleInsertData();
// RuleTestWithInsertDataAll();
Console.ReadKey();
}
public static void RuleTestWithSingleInsertData()
{
try
{
CustomRuleRepository repository = new CustomRuleRepository();
List<RuleEngineEntity> rules = new List<RuleEngineEntity>();
rules.Add(new RuleEngineEntity { FieldName = "Age", Name = "CustomerCheck", Value = 25 });
repository.LoadRuleForTest1(rules.FirstOrDefault());
//Compile rules
var factory = repository.Compile();
//Create a working session
var session = factory.CreateSession();
RuleEngineRequestModel ruleEngineRequestModel = new RuleEngineRequestModel
{
ruleList = rules,
customerData = new List<Customer>() { new Customer { Name = "A", Age = 19 },
new Customer { Name = "B", Age = 26 } }.ToArray()
};
session.InsertAll(ruleEngineRequestModel.customerData);
var IspassedorNot = session.Fire();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
public static void RuleTestWithInsertDataAll()
{
try
{
CustomRuleRepository repository = new CustomRuleRepository();
List<RuleEngineEntity> rules = new List<RuleEngineEntity>();
rules.Add(new RuleEngineEntity { FieldName = "Age", Name = "CustomerCheck", Value = 25 });
repository.LoadRuleForTest2(rules.FirstOrDefault());
//Compile rules
var factory = repository.Compile();
//Create a working session
var session = factory.CreateSession();
RuleEngineRequestModel ruleEngineRequestModel = new RuleEngineRequestModel
{
ruleList = rules,
customerData = new List<Customer>() { new Customer { Name = "A", Age = 28 },
new Customer { Name = "B", Age = 26 } }.ToArray()
};
session.InsertAll(ruleEngineRequestModel.customerData);
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 List<Customer> customerDataList { 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 bool SendEmail { 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 LoadRuleForTest1(RuleEngineEntity rule)
{
_ruleSet.Add(BuildRuleForTest1(rule));
}
public void LoadRuleForTest2(RuleEngineEntity rule)
{
_ruleSet.Add(BuildRuleForTest2(rule));
}
public List<IRuleDefinition> BuildRuleForTest1(RuleEngineEntity rule)
{
return Test1(rule);
}
public List<IRuleDefinition> BuildRuleForTest2(RuleEngineEntity rule)
{
return Test2(rule);
}
public List<IRuleDefinition> Test1(RuleEngineEntity rule)
{
RuleBuilder builder = new RuleBuilder();
builder.Name("DefaultRules");
try
{
var modelPattern = builder.LeftHandSide().Pattern(typeof(Customer), "CustomerCheck");
var modelParameter = modelPattern.Declaration.ToParameterExpression();
var expres = Expression.Property(modelParameter, rule.FieldName);
var binaryExpression = Expression.GreaterThan(expres, Expression.Constant(rule.Value));
LambdaExpression expressionCondition = Expression.Lambda(binaryExpression,
modelParameter);
modelPattern.Condition(expressionCondition);
Expression<Action<IContext, Customer, RuleEngineEntity>> action =
(ctx, CustomerCheck, rules) => FireActionAsync(ctx, CustomerCheck, rules);
builder.RightHandSide().Action(action);
}
catch (Exception e)
{
// throw new Exception(e.Message);
}
var buildRule = builder.Build();
return new List<IRuleDefinition> { buildRule };
}
public List<IRuleDefinition> Test2(RuleEngineEntity rule)
{
RuleBuilder builder = new RuleBuilder();
builder.Name("DefaultRules");
try
{
var modelPattern = builder.LeftHandSide().Pattern(typeof(RuleEngineRequestModel), "CustomerCheck");
var modelParameter = modelPattern.Declaration.ToParameterExpression();
var customerDataInArray = Expression.Property(modelParameter, nameof(RuleEngineRequestModel.customerData));
var indx = Expression.Parameter(typeof(int), "index");
var customerData = Expression.ArrayIndex(customerDataInArray, indx);
var expres = Expression.Property(customerData, rule.FieldName);
var binaryExpression = Expression.GreaterThan(expres, Expression.Constant(rule.Value));
LambdaExpression expressionCondition = Expression.Lambda(binaryExpression,
modelParameter);
modelPattern.Condition(expressionCondition);
Expression<Action<IContext, Customer>> action =
(ctx, CustomerCheck) => FireActionAsync(ctx, CustomerCheck, null);
builder.RightHandSide().Action(action);
}
catch (Exception e)
{
// throw new Exception(e.Message);
}
var buildRule = builder.Build();
return new List<IRuleDefinition> { buildRule };
}
public void FireActionAsync(IContext ctx, Customer customer, RuleEngineEntity rule=null)
{
Console.WriteLine($"{rule.Name} Triggered");
}
}
Error: variable 'index' of type 'System.Int32' referenced from scope '', but it is not defined
Expected: want to validate rule against array data with dynamic indexer.
回答1:
At a quick glance, it appears that you are passing in an array of Customers when inserting the facts into the session, but the rule is looking for the RuleEngineRequestModel.
Also, side note - why are you initialising an array as a List, then converting to an array, rather than just initialising as an array? i.e.
var ruleEngineRequestModel = new RuleEngineRequestModel
{
ruleList = rules,
customerData = {
new Customer { Name = "A", Age = 28 },
new Customer { Name = "B", Age = 26 }
}
};
Finally, why are you inserting the rules at the same time as the data? That seems like it would cause more headaches than benefits, especially seeing as your runtime rule builder ignores them entirely.
EDIT: Having had a chance to see the updated code, this confirms my suspicions - if you use Rule 1 for both tests, it should work (rather, it will fire for each individual customer). The reason why Rule 2 never works is because it's expecting a RuleEngineRequestModel, but you are passing the IEnumerable in directly. Either pass in the request model directly and continue to use Rule 2, or scrap Rule 2 entirely.
来源:https://stackoverflow.com/questions/57722215/accessing-elements-of-types-with-runtime-indexers-using-expression-trees