• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

microsoft / RulesEngine / 13618601570

02 Mar 2025 07:27PM UTC coverage: 94.903% (+0.9%) from 93.976%
13618601570

push

github

web-flow
[Bug Fix] : Methods on custom objects not available due to System.Linq.Dynamic.Core update (#661)

* short-circuiting based on toggle

* unit test cases fix

* demoprogram

* coverage

* removal of extra comma

* version update

302 of 374 branches covered (80.75%)

66 of 67 new or added lines in 2 files covered. (98.51%)

5 existing lines in 1 file now uncovered.

633 of 667 relevant lines covered (94.9%)

308.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.91
/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs
1
// Copyright (c) Microsoft Corporation.
2
//  Licensed under the MIT License.
3

4
using FastExpressionCompiler;
5
using RulesEngine.HelperFunctions;
6
using RulesEngine.Models;
7
using System;
8
using System.Collections.Generic;
9
using System.Linq;
10
using System.Linq.Dynamic.Core;
11
using System.Linq.Dynamic.Core.Exceptions;
12
using System.Linq.Dynamic.Core.Parser;
13
using System.Linq.Expressions;
14
using System.Reflection;
15
using System.Text.RegularExpressions;
16

17
namespace RulesEngine.ExpressionBuilders
18
{
19
    public class RuleExpressionParser
20
    {
21
        private readonly ReSettings _reSettings;
22
        private readonly IDictionary<string, MethodInfo> _methodInfo;
23

24
        public RuleExpressionParser(ReSettings reSettings = null)
291✔
25
        {
26
            _reSettings = reSettings ?? new ReSettings();
291!
27
            _methodInfo = new Dictionary<string, MethodInfo>();
291✔
28
            PopulateMethodInfo();
291✔
29
        }
291✔
30

31
        private void PopulateMethodInfo()
32
        {
33
            var dict_add = typeof(Dictionary<string, object>).GetMethod("Add", BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(string), typeof(object) }, null);
291✔
34
            _methodInfo.Add("dict_add", dict_add);
291✔
35
        }
291✔
36

37
        public Expression Parse(string expression, ParameterExpression[] parameters, Type returnType)
38
        {
39
            var config = new ParsingConfig {
633✔
40
                CustomTypeProvider = new CustomTypeProvider(_reSettings.CustomTypes),
633✔
41
                IsCaseSensitive = _reSettings.IsExpressionCaseSensitive
633✔
42
            };
633✔
43

44
            // Instead of immediately returning default values, allow for expression parsing to handle dynamic evaluation.
45
            try
46
            {
47
                return new ExpressionParser(parameters, expression, Array.Empty<object>(), config).Parse(returnType);
633✔
48
            }
49
            catch (ParseException)
51✔
50
            {
51
                if (_reSettings.EnableExceptionAsErrorMessageForRuleExpressionParsing)
51!
52
                {
53
                    throw;
51✔
54
                }
UNCOV
55
                return Expression.Constant(GetDefaultValueForType(returnType));
×
56
            }
57
            catch (Exception)
15✔
58
            {
59
                throw;
15✔
60
            }
61
        }
567✔
62

63
        private object GetDefaultValueForType(Type type)
64
        {
UNCOV
65
            if (type == typeof(bool))
×
UNCOV
66
                return false;
×
UNCOV
67
            if (type == typeof(int) || type == typeof(float) || type == typeof(double))
×
68
                return int.MinValue;
×
UNCOV
69
            return null;
×
70
        }
71

72
        public Func<object[], T> Compile<T>(string expression, RuleParameter[] ruleParams)
73
        {
74
            var rtype = typeof(T);
492✔
75
            if (rtype == typeof(object))
492✔
76
            {
77
                rtype = null;
42✔
78
            }
79
            var parameterExpressions = GetParameterExpression(ruleParams).ToArray();
492✔
80

81
            var e = Parse(expression, parameterExpressions, rtype);
492✔
82
            if (rtype == null)
435✔
83
            {
84
                e = Expression.Convert(e, typeof(T));
42✔
85
            }
86
            var expressionBody = new List<Expression>() { e };
435✔
87
            var wrappedExpression = WrapExpression<T>(expressionBody, parameterExpressions, new ParameterExpression[] { });
435✔
88
            return CompileExpression(wrappedExpression);
435✔
89

90
        }
91

92
        private Func<object[], T> CompileExpression<T>(Expression<Func<object[], T>> expression)
93
        {
94
            if (_reSettings.UseFastExpressionCompiler)
546✔
95
            {
96
                return expression.CompileFast();
480✔
97
            }
98
            return expression.Compile();
66✔
99
        }
100

101
        private Expression<Func<object[], T>> WrapExpression<T>(List<Expression> expressionList, ParameterExpression[] parameters, ParameterExpression[] variables)
102
        {
103
            var argExp = Expression.Parameter(typeof(object[]), "args");
546✔
104
            var paramExps = parameters.Select((c, i) => {
546✔
105
                var arg = Expression.ArrayAccess(argExp, Expression.Constant(i));
1,164✔
106
                return (Expression)Expression.Assign(c, Expression.Convert(arg, c.Type));
1,164✔
107
            });
546✔
108
            var blockExpSteps = paramExps.Concat(expressionList);
546✔
109
            var blockExp = Expression.Block(parameters.Concat(variables), blockExpSteps);
546✔
110
            return Expression.Lambda<Func<object[], T>>(blockExp, argExp);
546✔
111
        }
112

113
        internal Func<object[], Dictionary<string, object>> CompileRuleExpressionParameters(RuleParameter[] ruleParams, RuleExpressionParameter[] ruleExpParams = null)
114
        {
115
            ruleExpParams = ruleExpParams ?? new RuleExpressionParameter[] { };
111!
116
            var expression = CreateDictionaryExpression(ruleParams, ruleExpParams);
111✔
117
            return CompileExpression(expression);
111✔
118
        }
119

120
        public T Evaluate<T>(string expression, RuleParameter[] ruleParams)
121
        {
122
            var func = Compile<T>(expression, ruleParams);
45✔
123
            return func(ruleParams.Select(c => c.Value).ToArray());
84✔
124
        }
125

126
        private IEnumerable<Expression> CreateAssignedParameterExpression(RuleExpressionParameter[] ruleExpParams)
127
        {
128
            return ruleExpParams.Select((c, i) => {
111✔
129
                return Expression.Assign(c.ParameterExpression, c.ValueExpression);
138✔
130
            });
111✔
131
        }
132

133
        // <summary>
134
        /// Gets the parameter expression.
135
        /// </summary>
136
        /// <param name="ruleParams">The types.</param>
137
        /// <returns></returns>
138
        /// <exception cref="ArgumentException">
139
        /// types
140
        /// or
141
        /// type
142
        /// </exception>
143
        private IEnumerable<ParameterExpression> GetParameterExpression(RuleParameter[] ruleParams)
144
        {
145
            foreach (var ruleParam in ruleParams)
3,108✔
146
            {
147
                if (ruleParam == null)
1,062!
148
                {
NEW
149
                    throw new ArgumentException($"{nameof(ruleParam)} can't be null.");
×
150
                }
151

152
                yield return ruleParam.ParameterExpression;
1,062✔
153
            }
154
        }
492✔
155

156
        private Expression<Func<object[], Dictionary<string, object>>> CreateDictionaryExpression(RuleParameter[] ruleParams, RuleExpressionParameter[] ruleExpParams)
157
        {
158
            var body = new List<Expression>();
111✔
159
            var paramExp = new List<ParameterExpression>();
111✔
160
            var variableExp = new List<ParameterExpression>();
111✔
161

162

163
            var variableExpressions = CreateAssignedParameterExpression(ruleExpParams);
111✔
164

165
            body.AddRange(variableExpressions);
111✔
166

167
            var dict = Expression.Variable(typeof(Dictionary<string, object>));
111✔
168
            var add = _methodInfo["dict_add"];
111✔
169

170
            body.Add(Expression.Assign(dict, Expression.New(typeof(Dictionary<string, object>))));
111✔
171
            variableExp.Add(dict);
111✔
172

173
            for (var i = 0; i < ruleParams.Length; i++)
630✔
174
            {
175
                paramExp.Add(ruleParams[i].ParameterExpression);
204✔
176
            }
177
            for (var i = 0; i < ruleExpParams.Length; i++)
498✔
178
            {
179
                var key = Expression.Constant(ruleExpParams[i].ParameterExpression.Name);
138✔
180
                var value = Expression.Convert(ruleExpParams[i].ParameterExpression, typeof(object));
138✔
181
                variableExp.Add(ruleExpParams[i].ParameterExpression);
138✔
182
                body.Add(Expression.Call(dict, add, key, value));
138✔
183

184
            }
185
            // Return value
186
            body.Add(dict);
111✔
187

188
            return WrapExpression<Dictionary<string, object>>(body, paramExp.ToArray(), variableExp.ToArray());
111✔
189
        }
190
    }
191
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc