Table of Contents

Expression Context

ExpressionContext (or AsyncExpressionContext) at NCalcAsync, is the class responsible for storing contextual data of the expression, like parameters and functions.

Creating a context

//Explicit
var context = new ExpressionContext
{
    Options = ExpressionOptions.IgnoreCaseAtBuiltInFunctions
};
var expression = new Expression("ABS(42)", context);

//Implicit
ExpressionContext context = ExpressionOptions.IgnoreCaseAtBuiltInFunctions;
var expression = new Expression("ABS(42)", context);

//Cloning
ExpressionContext context = ExpressionOptions.IgnoreCaseAtBuiltInFunctions;
var newContext = context with { CultureInfo = CultureInfo.CurrentUICulture };
var expression = new Expression("ABS(42)", newContext);
Warning

The with keyword will create a shallow copy of the object, any unchanged reference type like IDictionary inside the context will remain the same.

Using a single context for all expressions

You can optimize your system to use a single context instance for all expressions. The example below use DI for creating a singleton instance.

///At Program.cs:
builder.Services.AddSingleton<ExpressionContext>(_ =>
{
    return new ExpressionContext
    {
        Options = ExpressionOptions.OverflowProtection,
        Functions = new Dictionary<string, ExpressionFunction>()
        {
            {"SecretOperation", (arguments) => 42}
        }.ToFrozenDictionary()
    };
});

///At MyService.cs
public class MyService(IExpressionFactory expressionFactory, ExpressionContext context)
{
    public object? Evaluate(string expressionString)
    {
        //Using the `with` keyword is possible to create a copy with different options.
        var expression = expressionFactory.Create(expressionString, context with { Options = ExpressionOptions.IgnoreCaseAtBuiltInFunctions });
        return expression.Evaluate();
    }
}

If you are not using DI, you can also use a static property to store a single context:

///At MyContext.cs:
public class MyContext
{
    public static readonly ExpressionContext Value = new();
}

///At MyService.cs
public class MyService
{
    public object? Evaluate(string expressionString)
    {
        var expression = new Expression(expressionString, MyContext.Value with { Options = ExpressionOptions.IgnoreCaseAtBuiltInFunctions });
        return expression.Evaluate();
    }
}