Я пытаюсь динамически создавать выражения для механизма правил, и все шло очень хорошо, пока я не попытался разрешить вложенные типы для списка.
Мой пример приложения выдает ошибку приведения W / ниже кода образца:
Кроме того, мне нужно сделать мой RuleEngine logi c generi c таким, чтобы я мог взять List.List
Есть рекомендации?
public class Container
{
public string Repository {get;set; } = "TestRepo";
public List<Shipment> Shipments { get;set;} = new List<Shipment>();
}
public class Shipment
{
public int OrderNumber { get; set; }
}
Вот мой класс правил
public class RuleEx
{
public string MemberName { get; set; }
public string Operator { get; set; }
public string TargetValue { get; set; }
}
Here is my Rule Engine
public class RuleEngine
{
public bool PassesRules<T>(List<RuleEx> rules, T toInspect)
{
return this.CompileRules<T>(rules).Invoke(toInspect);
}
public Func<T, bool> CompileRule<T>(RuleEx r)
{
var paramUser = Expression.Parameter(typeof(T));
Expression expr = BuildExpr<T>(r, paramUser);
return Expression.Lambda<Func<T, bool>>(expr, paramUser).Compile();
}
public Func<T, bool> CompileRules<T>(IList<RuleEx> rules)
{
var paramUser = Expression.Parameter(typeof(T));
List<Expression> expressions = new List<Expression>();
foreach (var r in rules)
{
expressions.Add(BuildExpr<T>(r, paramUser));
}
var expr = AndExpressions(expressions);
return Expression.Lambda<Func<T, bool>>(expr, paramUser).Compile();
}
Expression AndExpressions(IList<Expression> expressions)
{
if (expressions.Count == 1)
return expressions[0];
Expression exp = Expression.And(expressions[0], expressions[1]);
for (int i = 2; expressions.Count > i; i++)
{
exp = Expression.And(exp, expressions[i]);
}
return exp;
}
Expression BuildExpr<T>(RuleEx r, ParameterExpression param)
{
Expression propExpression;
Type propType;
ExpressionType tBinary;
if (r.MemberName.Contains('.'))
{
String[] childProperties = r.MemberName.Split('.');
var property = typeof(T).GetProperty(childProperties[0]);
var paramExp = Expression.Parameter(typeof(T), "SomeObject");
propExpression = Expression.PropertyOrField(param, childProperties[0]);
for (int i = 1; i < childProperties.Length; i++)
{
property = property.PropertyType.GetProperty(childProperties[i]);
propExpression = Expression.PropertyOrField(propExpression, childProperties[i]);
}
propType = propExpression.Type;
}
else
{
propExpression = Expression.PropertyOrField(param, r.MemberName);
propType = propExpression.Type;
}
// is the operator a known .NET operator?
if (ExpressionType.TryParse(r.Operator, out tBinary))
{
var right = Expression.Constant(Convert.ChangeType(r.TargetValue, propType));
// use a binary operation, e.g. 'Equal' -> 'u.Age == 15'
return Expression.MakeBinary(tBinary, propExpression, right);
}
else
{
var method = propType.GetMethod(r.Operator);
var tParam = method.GetParameters()[0].ParameterType;
var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tParam));
// use a method call, e.g. 'Contains' -> 'u.Tags.Contains(some_tag)'
return Expression.Call(propExpression, method, right);
}
}
}
Program.CS
var Shipments = new List<Shipment>()
{
new Shipment(){ OrderNumber = 555 },
new Shipment(){ OrderNumber = 200 },
new Shipment(){ OrderNumber = 200 }
};
Container container = new Container()
{
Repository = "TestRepo",
Shipments = Shipments
};
RuleEngine mr = new RuleEngine();
var rules = new List<RuleEx>() { new RuleEx() { MemberName = "Shipment.OrderNumber", Operator = "LessThanOrEqual", TargetValue = 250 } };
var pases = mr.PassesRules<Container>(rules, container);