microsoft / RulesEngine

A Json based Rules Engine with extensive Dynamic expression support
https://microsoft.github.io/RulesEngine/
MIT License
3.47k stars 530 forks source link

IEnumerable<T>.Average() doesn't work with OutputExpression Action #539

Closed APrinesdomu closed 8 months ago

APrinesdomu commented 8 months ago

The rules engine doesn't seem to recognize IEnumerable.Average(), IEnumerable.Sum(), and other aggregate Linq expressions. No result is produced when these are used as OutputExpressions; however, as they are valid linq expressions this should work.

Please see sample rules and test data below to reproduce the issue:

Rules { "WorkflowName": "Average Earnings", "globalParams": [ { "Name": "YrsInAverage", "Expression": "5" } ], "Rules": [ { "RuleName": "Final Average earnings", "SuccessEvent": "AvergeEarnings", "ErrorMessage": "Final Average earnings rule failed.", "ErrorType": "Error", "RuleExpressionType": "LambdaExpression", "Expression": "input1.earnings != null && input1.earnings.Count > 0 && YrsInAverage > 0", "Actions": { "OnSuccess": { "Name": "OutputExpression", "Context": { "Expression": "input1.earnings.Reverse().Take(YrsInAverage).Average()" } } } }, { "RuleName": "Best Average earnings", "SuccessEvent": "AvergeEarnings", "ErrorMessage": "Best Average earnings rule failed.", "ErrorType": "Error", "RuleExpressionType": "LambdaExpression", "Expression": "input1.earnings != null && input1.earnings.Count > 0 && YrsInAverage > 0", "Actions": { "OnSuccess": { "Name": "OutputExpression", "Context": { "Expression": "input1.earnings.OrderByDescending(x => x).Take(YrsInAverage).Average()" } } } } ] }

Test Data [ { "workflows": [ "Eligibility", "Time to Retirement", "Average Earnings" ], "id": "1234", "age": 67, "service": 2.1, "earnings": [ 60000, 62000 ] }, { "workflows": [ "Eligibility", "Time to Retirement", "Average Earnings" ], "id": "2345", "age": 60.5, "service": 9.5, "earnings": [ 70000, 72000, 85000, 88000, 91000, 98000, 70000, 72000, 85000 ] }, { "workflows": [ "Eligibility", "Time to Retirement", "Average Earnings" ], "id": "3456", "age": 56, "service": 29.25, "earnings": [ 70000, 72000, 85000, 88000, 91000, 98000, 70000, 72000, 85000, 70000, 72000, 85000, 70000, 72000, 85000, 70000, 72000, 85000, 91000, 93000, 96000, 101000, 105000, 108000, 120000, 123000, 90000, 95000, 98000 ]} ]

abbasc52 commented 8 months ago

@APrinesdomu RulesEngine supports a subset of linq operations as defined here - https://dynamic-linq.net/expression-language#sequence-operators

In your example, Reverse() is not supported and Average requires passing a lambda i.e. Average(x => x)

Here is a working example with small modifications to your Rules - https://dotnetfiddle.net/dfAf01

If you need to perform operations which are not supported, you can create a custom class with methods , inject it in settings and use it Rules as a workaround.

APrinesdomu commented 8 months ago

Thanks so much, that resolved the issue. If custom operations are needed the custom class would extend the ActionBase class, correct?