Open balajiforgithub opened 5 years ago
I'm just experimenting with how to solve the same issue and this seems to work nicely
public class Rule
{
public string Id {get;set;}
public string Expression {get;set;}
}
public interface IExpressionCache
{
IGenericExpression<bool> GetExpression(Rule rule, Context context);
}
public interface IExpressionCompiler
{
IGenericExpression<bool> Compiler(string expression, Context context);
bool Execute(IGenericExpression<bool> compiled, Context context);
}
public class ExpressionCache : IExpressionCache
{
private readonly IExpressionCompiler _engine;
private readonly ICache _cache;
private static object locker = new object();
public ExpressionCache(IExpressionCompiler engine, ICache cache)
{
_engine = engine;
_cache = cache;
}
public IGenericExpression<bool> GetExpression(Rule rule, Context context)
{
var key = $"rule_{rule.Id}";
// https://en.wikipedia.org/wiki/Double-checked_locking
var compiled = _cache.Get<IGenericExpression<bool>>(key);
if (compiled == null) // 1st check
{
lock (locker) // Enter critical section
{
compiled = _cache.Get<IGenericExpression<bool>>(key);
if (compiled == null) // 2nd (double) check
{
// Create a new context and cache it
compiled = _engine.Compiler(rule.Expression, context);
_cache.Set(key, compiled);
}
}
}
return compiled;
}
}
public class ExpressionCompiler : IExpressionCompiler
{
private const string _contextVariableName = "Context";
public IGenericExpression<bool> Compiler(string expression, Context context)
{
var ec = new ExpressionContext();
ec.Variables.Add(_contextVariableName, context);
return ec.CompileGeneric<bool>(expression);
}
public bool Execute(IGenericExpression<bool> compiled, Context context)
{
compiled.Context.Variables[_contextVariableName] = context;
return compiled.Evaluate();
}
}
public interface ICache
{
T Get<T>(string key);
void Set<T>(string key, T value);
}
/// <summary>
/// Naive cache
/// TODO: configure
/// </summary>
public class Cache : ICache
{
private readonly IMemoryCache _cache;
public Cache(IMemoryCache cache)
{
_cache = cache;
}
public T Get<T>(string key)
{
return _cache.Get<T>(key);
}
public void Set<T>(string key, T value)
{
_cache.Set(key, value, TimeSpan.FromSeconds(5));
}
}
I've also implemented a cache with a simple Dictionary. I think the application would best know how to do the caching
Hi, I am using Flee.Net45 library for evaluating expressions in my application.
As rule compilation is taking time, can we cache the compiled rules to bypass the rule compilation process?
Sample code : _expressionContext.CompileDynamic("expression")
Appreciate your help.
Thanks, Balaji.