Then()
.Do(ctx => SendEmailNotification(currentCartItem));
Is there a way to properly remove a fact from the sessions memory once inserted?
Simply calling Remove
/ TryRemove
doesn't actually free any memory...
Minimized example of what I am currently trying to do:
using System;
using NRules;
using NRules.Fluent;
using NRules.Fluent.Dsl;
namespace Playground
{
class Fact
{
public int data { get; set; }
}
public class TestRule : Rule
{
public override void Define()
{
Fact fact = null;
When()
.Match<Fact>(() => fact);
Then()
.Do(ctx => Console.WriteLine("fact " + fact.data));
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("press any key to start...");
Console.ReadKey();
var repository = new RuleRepository();
repository.Load(x => x.From(typeof(Program).Assembly));
var factory = repository.Compile();
var session = factory.CreateSession();
Console.WriteLine("inserting facts...\n");
for (int i = 0; i < 1000000; ++i)
{
session.Insert(new Fact { data = i });
}
// memory usage has now grown to ~800 MB
Console.WriteLine("press any key to retract facts...");
Console.ReadKey();
foreach (var fact in session.Query<Fact>())
{
if (!session.TryRetract(fact))
{
Console.WriteLine("retracting " + fact.data + " failed"); // no output -> no errors
}
}
foreach (var fact in session.Query<Fact>())
{
Console.WriteLine("still in memory: " + fact.data + ")"); // no output -> no facts in session
}
// memory usage hasn't dropped down again
Console.WriteLine("\nPress any key to continue...");
Console.ReadKey();
}
}
}
rf => rf.ReferenceB == myNameFact
and rf => rf.ReferenceB == otherNameFact
are the same. The reason the issue goes away as soon as you change or add expressions, is that the opportunity for node sharing goes away. I'm working on fixing it.
public class LogLevelRule : Rule
{
public static ILog Log = LogManager.GetLogger("MyLogger");
public override void Define()
{
string logLevel = default;
When()
.Query(() => logLevel, q => q
.From(() => GetCurrentLogLevel(Log)));
Then()
.Do(x => Console.WriteLine($"Logging at {logLevel} with {Log.Logger.Name}"));
}
private string GetCurrentLogLevel(ILog logger)
{
if(logger.IsDebugEnabled)
return "Debug";
if(logger.IsInfoEnabled)
return "Info";
if(logger.IsWarnEnabled)
return "Warn";
if(logger.IsErrorEnabled)
return "Error";
if(logger.IsFatalEnabled)
return "Fatal";
return "None";
}
}
We are planning on using NRules as an engine in a multi-tenant environment. As such, we would like to keep the rules repo (and the associated factories) separated. In the same process.
We would setup a dictionary of tenants and their associated rules repo/factory. When a fact is generated in the app, it would pass it to the process. We will search the dictionary for the tenant. We would then use the factory returned for the tenant to setup a session and process the fact.
Do you guys think there are any problems with this design?