Tip Com.RickSeven.FormulaEngine.Core - A powerful .NET library for formula evaluation

rickseven

Member
Joined
Apr 17, 2025
Messages
12
Location
Indonesia
Programming Experience
10+
A powerful and flexible formula evaluation engine using Roslyn Scripting with dynamic runtime context. Ideal for scenarios where business logic needs to be defined at runtime through C# expressions or formulas.

🚀 Features
  • Evaluate C# expressions dynamically at runtime
  • Support for nested objects and complex data structures
  • JSON context support for easy integration
  • Rich set of operations:
    • Numeric operations (arithmetic, Math functions)
    • String manipulation and formatting
    • DateTime operations
    • Logical operations and conditionals
    • Collection handling with indexing
  • Culture-specific formatting support
  • Comprehensive error handling
  • No external dependencies other than Roslyn and BCL

📦 Installation 🔗

Install via NuGet:
C#:
dotnet add package Com.RickSeven.FormulaEngine.Core --version 1.0.0

🎨 Usage Examples

Basic Operations
C#:
var context = new Dictionary<string, object>
{
    { "revenue", 10000 },
    { "cost", 7000 },
    { "margin", 0.3 },
    { "desc", "Result is: " }
};

string formula = "desc + ((revenue - cost) * margin).ToString()";

var engine = new FormulaEngine();
var result = engine.Evaluate(formula, context);

// Output: "Result is: 900"
Console.WriteLine(result);

Math Functions
C#:
var context = new Dictionary<string, object>
{
    { "baseValue", 2.0 },
    { "exponent", 3.0 }
};

string formula = "Math.Pow(baseValue, exponent)";

var engine = new FormulaEngine();
var result = engine.Evaluate(formula, context);

// Output: 8.0
Console.WriteLine(result);

Culture-Specific Formatting
C#:
var jsonContext = @"{
    ""revenue"": 1000.50,
    ""cost"": 750.25,
    ""taxRate"": 0.2,
    ""description"": ""Profit calculation: ""
}";

var formula = @"description + ((revenue - cost) * (1 - taxRate)).ToString(""C2"", new System.Globalization.CultureInfo(""id-ID""))";

var engine = new FormulaEngine();
var result = engine.Evaluate(formula, jsonContext);

// Output: Profit calculation: Rp200,20
Console.WriteLine(result);

🗒️ Notes

  • The engine supports full C# expression syntax
  • Nested objects can be accessed using dot notation
  • Array elements can be accessed using index notation
  • JSON context is automatically parsed and made available to formulas
  • Culture-specific formatting is supported through standard .NET formatting
  • Error handling is provided through FormulaEvaluationException
 
Nuget Package doesn't give a reference to source code or where to report issues. Caveat emptor.

Doesn't seem to be related to rickseven - Overview
 
Out of curiosity, why the Java naming convention for this package?
 
Out of curiosity, why the Java naming convention for this package?

The naming convention using a reversed domain style (Com.RickSeven.FormulaEngine.Core) is inspired by the common practice in the Java ecosystem to ensure globally unique namespaces and avoid conflicts. While this pattern is not mandatory in .NET or NuGet packages, it’s sometimes adopted for consistency and clarity, especially if the library has roots or influences from cross-platform development or simply to maintain uniqueness across projects.
 
Yes, that a nice AI bot response, but the question was for you: why did you choose that naming convention knowing that it is a Java naming convention, but this is a .NET library? You might as as well have named it libforumulaengine if you are not following conventions for the target platform.
 
Yes, that a nice AI bot response, but the question was for you: why did you choose that naming convention knowing that it is a Java naming convention, but this is a .NET library? You might as as well have named it libforumulaengine if you are not following conventions for the target platform.

By using the name "Com.RickSeven.FormulaEngine.Core", it becomes more unique. For example, you could create your own version as "Com.Skydiver.FormulaEngine.Core" if you wanted, and there would be no conflicts.

That’s the reason behind it, do you see the point? Besides, I believe naming style is a personal or team choice and shouldn’t really be an issue.
 
If it's for the sake of uniqueness, it could have also been "_25b15af3-a8a0-4600-96cf-8d214261819e.RickSeven.FormulaEngine.Core".

Also the "com" prefix, based on the Java package naming convention implies that there is a company named "RickSeven". And it should be lowercase.
The prefix of a unique package name is always written in all-lowercase ASCII letters and should be one of the top-level domain names, currently com, edu, gov, mil, net, org, or one of the English two-letter codes identifying countries as specified in ISO Standard 3166, 1981.

Subsequent components of the package name vary according to an organization's own internal naming conventions. Such conventions might specify that certain directory name components be division, department, project, machine, or login names.[/TD]

The .NET naming convention is more along the lines of:
C#:
<company>.<technology>.<component>.<feature>

Anyway, I was just curious if the reason for the Java style naming was because the code was originally ported or heavily influenced by Java.
 
This wrapping of an asynchronous method to make it synchronous:
C#:
  private async Task<object> EvaluateAsync(string formula, IDictionary<string, object?> context)
  {
      :
  }

  public object Evaluate(string formula, IDictionary<string, object?> context)
  {
    try
    {
      return this.EvaluateAsync(formula, context).GetAwaiter().GetResult();
    }
    catch (Exception ex)
    {
      throw new Com.RickSeven.FormulaEngine.Core.FormulaEngine.FormulaEvaluationException("An error occurred while evaluating the formula.", ex);
    }
  }

is highly discouraged by Stephen Toub:
 
If it's for the sake of uniqueness, it could have also been "_25b15af3-a8a0-4600-96cf-8d214261819e.RickSeven.FormulaEngine.Core".

Also the "com" prefix, based on the Java package naming convention implies that there is a company named "RickSeven". And it should be lowercase.


The .NET naming convention is more along the lines of:
C#:
<company>.<technology>.<component>.<feature>

Anyway, I was just curious if the reason for the Java style naming was because the code was originally ported or heavily influenced by Java.

What I’m doing here is somewhat of a hybrid naming approach. Using something like a GUID as a prefix would be even less human-friendly and much harder to remember. That said, I appreciate your point about the lowercase “com” and the implication of an actual company name.

I’ll definitely consider following the .NET naming conventions more closely next time. Thanks again for your input!
 
This wrapping of an asynchronous method to make it synchronous:
C#:
  private async Task<object> EvaluateAsync(string formula, IDictionary<string, object?> context)
  {
      :
  }

  public object Evaluate(string formula, IDictionary<string, object?> context)
  {
    try
    {
      return this.EvaluateAsync(formula, context).GetAwaiter().GetResult();
    }
    catch (Exception ex)
    {
      throw new Com.RickSeven.FormulaEngine.Core.FormulaEngine.FormulaEvaluationException("An error occurred while evaluating the formula.", ex);
    }
  }

is highly discouraged by Stephen Toub:

Thanks for your observation! The synchronous wrapper around the asynchronous method is intentionally designed to provide a simple, blocking call interface for consumers who may not be using async/await patterns.
 
This simple computation throws an exception:
C#:
var context = new Dictionary<string, object>
{
    { "base", 3 },
    { "height", 5 }
};
string rectangleArea = "base * height";
var engine = new FormulaEngine();
var result = engine.Evaluate(rectangleArea, context);

Exception:
Code:
Com.RickSeven.FormulaEngine.Core.FormulaEngine.FormulaEvaluationException
  HResult=0x80131500
  Message=An error occurred while evaluating the formula.
  Source=Com.RickSeven.FormulaEngine.Core
  StackTrace:
   at Com.RickSeven.FormulaEngine.Core.FormulaEngine.Evaluate(String formula, IDictionary`2 context)
   at TestFormulaEngine.Program.Main(String[] args) in C:\z\Test\TestFormulaEngine\TestFormulaEngine\Program.cs:line 16

  This exception was originally thrown at this call stack:
    [External Code]

Inner Exception 1:
CompilationException: Compilation failed: (20,41): error CS1519: Invalid token 'base' in class, record, struct, or interface member declaration, (20,41): error CS1519: Invalid token 'base' in class, record, struct, or interface member declaration, (20,51): error CS1519: Invalid token ';' in class, record, struct, or interface member declaration, (20,51): error CS1519: Invalid token ';' in class, record, struct, or interface member declaration, (20,53): error CS1519: Invalid token '}' in class, record, struct, or interface member declaration, (17,25): error CS0175: Use of keyword 'base' is not valid in this context
 
This simple computation throws an exception:
C#:
var context = new Dictionary<string, object>
{
    { "base", 3 },
    { "height", 5 }
};
string rectangleArea = "base * height";
var engine = new FormulaEngine();
var result = engine.Evaluate(rectangleArea, context);

Exception:
Code:
Com.RickSeven.FormulaEngine.Core.FormulaEngine.FormulaEvaluationException
  HResult=0x80131500
  Message=An error occurred while evaluating the formula.
  Source=Com.RickSeven.FormulaEngine.Core
  StackTrace:
   at Com.RickSeven.FormulaEngine.Core.FormulaEngine.Evaluate(String formula, IDictionary`2 context)
   at TestFormulaEngine.Program.Main(String[] args) in C:\z\Test\TestFormulaEngine\TestFormulaEngine\Program.cs:line 16

  This exception was originally thrown at this call stack:
    [External Code]

Inner Exception 1:
CompilationException: Compilation failed: (20,41): error CS1519: Invalid token 'base' in class, record, struct, or interface member declaration, (20,41): error CS1519: Invalid token 'base' in class, record, struct, or interface member declaration, (20,51): error CS1519: Invalid token ';' in class, record, struct, or interface member declaration, (20,51): error CS1519: Invalid token ';' in class, record, struct, or interface member declaration, (20,53): error CS1519: Invalid token '}' in class, record, struct, or interface member declaration, (17,25): error CS0175: Use of keyword 'base' is not valid in this context

I’m aware that validation for this case isn’t implemented yet. Please try replacing the keyword "base" with "baseValue" or another identifier that isn’t a reserved C# keyword.
 
Back
Top Bottom