Bonjour à tous,
Je suis en train de réaliser un moteur de règle pour les besoins d'un projet et je rencontre 2 soucis.
1)Le premier est la création de règle, celles-ci sont stockées dans une base de données SQL puis récupérer dans un service windows (au démarrage et lors de la modification d'une des règles via le back office).
Les règles sont de la forme :
Name Operator Target Nom NotEqual toto Code.Length Equal 5 DateNaissance GreaterThan 01/01/1900
Name, Code et DateNaissance correspondent aux attributs de ma classe.
Afin de créer mes règles côtés C# voilà les 2 procédures que j'utilise :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 public static Func<T, bool> CompileRule<T>(Rule r) { var paramUser = Expression.Parameter(typeof(T)); Expression expr = BuildExpr<T>(r, paramUser); return Expression.Lambda<Func<T, bool>>(expr, paramUser).Compile(); }Lorsque je viens pour créer mes règles 1 et 3, pas de soucis cela fonctionne mais pour la règle 2 il y a une erreur qui dit que Code.Length n'est pas un membre de ma classe. Cela est normal car Length est une propriété de la classe string pour retourner la longueur de la chaîne. J'ai regardé sur le net pour pouvoir retourner les propriétés imbriqués pour une classe mais je ne trouve pas de solution. L'erreur se produit dans la 2ème procédure sur la ligne
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 static Expression BuildExpr<T>(Rule r, ParameterExpression param) { var left = MemberExpression.Property(param, r.MemberName); var tProp = typeof(T).GetProperty(r.MemberName).PropertyType; ExpressionType tBinary; if (ExpressionType.TryParse(r.Operator, out tBinary)) { var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tProp)); return Expression.MakeBinary(tBinary, left, right); } else { var method = tProp.GetMethod(r.Operator); var tParam = method.GetParameters()[0].ParameterType; var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tParam)); return Expression.Call(left, method, right); } }Pour le moment j'ai contourné le problème en ajoutant un attribut Code_Length dans ma classe, mais cela n'est pas propre si je dois créer un attribut pour chaque propriété. De plus, mes classes sont créer dynamiquement via la classe CSharpCodeProvider afin de pouvoir les recompiler en cas de modification depuis le back office.
Code : Sélectionner tout - Visualiser dans une fenêtre à part var left = MemberExpression.Property(param, r.MemberName);
2) Le second problème vient justement de la gestion dynamique de mes classes pour créer mes règles. La fonctionattendant un type T qui correspond à une classe or je ne peux pas le préciser physiquement puisque je gère plusieurs classes en mémoire.
Code : Sélectionner tout - Visualiser dans une fenêtre à part Func<T, bool> CompileRule<T>(Rule r)Mes classes sont créer comme ceci :
Code : Sélectionner tout - Visualiser dans une fenêtre à part Func<Personne, bool> compiledRule = CompileRule<Personne>(rule);
La seule solution que je vois, serait de gérer dynamiquement dans une classe statique, autant de "Func" que j'ai de classe mais cela va vite devenir compliquer si tout est dynamique.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 CSharpCodeProvider cl = new CSharpCodeProvider(); var cp = new CompilerParameters(); cp.GenerateInMemory = true; var res = cl.CompileAssemblyFromSource( cp, "using System; public class Personne \t{ public string Nom {get; set;} public string Code {get; set;} public DateTime DateNaissance {get; set;} public string Hello() { return \"Bonjour \" + Nom + \" !\"; } }" ); var type = res.CompiledAssembly.GetType("Personne"); var obj = Activator.CreateInstance(type);
La solution n'est pas définitive si quelqu'un m'en propose une meilleure.
Partager