## Where communities thrive

• Join over 1.5M+ people
• Join over 100K+ communities
• Free without limits
• Create your own community
##### Activity
RajkumarViven
@RajkumarViven
@snikolayev pls provide the solution
Sergiy Nikolayev
@snikolayev

I think in this case you are better off using forward chaining. You would need two rules for that. First, you have a rule similar to the one you had originally, where you match a single person along with their cars, and wrap the result in a new fact, which you yield from the right-hand side of the rule.

When()
.Match<Person>(() => person, p => p.Age < 30)
.Let(() => cars, () => person.Cars.Where(c => c.Year > 2016))
.Having(() => cars.Any());
Then()
.Yield(ctx => new YoungPersonWithNewCar(person, cars));

Then another rule matches these new facts and collects them.

When()
.Query(() => youngPeopleWithNewCars, q => q
.Match<YoungPersonWithNewCar>()
.Collect()
.Where(c => c.Any()));
Then()
.Do(ctx => DoSomethingWithNewCarsThatBelongToYoungPeople(youngPeopleWithNewCars));
RajkumarViven
@RajkumarViven
Thanks @snikolayev its working fine
@snikolayev can we use multiple Then() in Our rule because we need to call Multiple Actions based on conditions like
if age <30 we can call the action x()
else if age >30 we can call the action y()
RajkumarViven
@RajkumarViven
Is there any options to call the actions like that?
RajkumarViven
@RajkumarViven
?
RajkumarViven
@RajkumarViven
is there any option @snikolayev ?
Sergiy Nikolayev
@snikolayev
You would need to write separate rules that deal with different conditions.
RajkumarViven
@RajkumarViven
ok thanks for your update
larryPlayabl
@larryPlayabl

@snikolayev I saw that you're doing some work on rete optimization, such as this commit here:

NRules/NRules@6001f4a

If it'd be helpful, I'd be happy to try a new version with our project and provide a comparison using the Unity 3D Engine's profiler again, as I did in this issue:

NRules/NRules#200

If there are two commits you'd like me to do an A/B comparison with, just let me know.

Or I can wait a bit if there's more you're planning to do.

larryPlayabl
@larryPlayabl
Do you think that changes made in this commit will help with the performance issues I described in that issue?
Sergiy Nikolayev
@snikolayev
@larryPlayabl the optimizations I'm working on will reduce the amount of expression re-evaluations in the rete graph when propagating updates. This does not reduce allocations in join nodes, but you may be able to observe the improvements just due to the reduction of the number of joins. This is however just the first step in improving the performance of the rete graph, so I'm also planning to address allocations more directly.
larryPlayabl
@larryPlayabl
@snikolayev That makes sense. Glad to hear you're planning on continuing improvements, including focusing on allocations. Let me know if I can help with a round of profiling at some point.
przemekwojcik
@przemekwojcik
@snikolayev is there any easy way to find out because of which PatternElement a rule was fired? Once a rule is fired i have an ActionContext available, it has multiple properties. I see something like ActionContext.Rule.LeftHandSide.ChildElements[0]...Conditions[0] - is there a way to find out has that condition fired during processing of a fact?
larryPlayabl
@larryPlayabl

@przemekwojcik Check out ISession.Events.LhsExpressionEvaluatedEvent. This event fires every time a When (LHS) expression is evaluated, and references all rules that contain this expression. You can monitor these events over time to see what is matching for a given rule.

In a rete net, you have to do a bit of legwork to figure out exactly which pattern matches cause a rule to fire because expressions can be shared between multiple rules, and their results are cached.

Therefore you need to build some tooling to do monitor and associate evaluations with a given rule firing.

The NRules debugger visualizer can help to illustrate this for you.
Sergiy Nikolayev
@snikolayev
One thing I would like to add to what @larryPlayabl said is that a rule fires when ALL the patterns on the left-hand side matched. So, it's a bit incorrect to ask "which of them caused the rule to fire", because the answer is "all of them".
larryPlayabl
@larryPlayabl
Good point! I guess I was thinking about the case of an Or, in which case it's a bit less clear.
Paweł Adamczuk
@PawelAdamczuk
Hello! Could somebody point me to a resource that would explain why a concept of a RuleSet is needed? I am making a proof of concept for a product and I can just use a single Rule Set but I suppose this class exists for a reason. I'm pretty new to Business Rules in general.
Ivan Li
@ivanycli
hello. i'm a new nrule user. i wanted to know if it's possible for a rule to add an object into the ctx which can be used by another rule. i'm defining a series of checks across rules and looking for a way to capture all the results in single entity which i'm inserting into the context when the first rule fires. is it better to insert the various results into the ctx then do a session.query from the main caller? i'm open to better approaches to handling this scenario. thanks!
Sergiy Nikolayev
@snikolayev
@PawelAdamczuk a rule set is just a collection of rules. You can then decide which rules to compile based on which rule set they are in. The notion of the rule set does not exist at the DSL level or at execution time (when compiled to a rete graph). In the future rule sets may start playing a bigger role, but right now it's limited to just organizing and filtering rules at a rule model level.
@ivanycli have you seen docs on forward chaining? https://github.com/NRules/NRules/wiki/Forward-Chaining
Paweł Adamczuk
@PawelAdamczuk
Thank you!
Ivan Li
@ivanycli
Thanks @snikolayev , I'll review the forward chaining documentation
lucaszacutti
@lucaszacutti
Hi! I'm totally newy with nrules, Can I have nested rules? I would like to test a condition applicable to several nested rules and any nested rule with it own sub-condition and/or other nested rules. Is that possible?
larryPlayabl
@larryPlayabl
@lucaszacutti You can't have a nested rule, but you can have rule logic shared between multiple rules. One way to do this is with a base class to a rule. Another is to call utility methods or extension methods that add clauses to your rule. You'll need to pass any context into such utility methods. Check out this page for an example: https://github.com/NRules/NRules/wiki/DSL-Extensions
tnghub3
@tnghub3
Does it support decision table?
Similar to decision table with drools and brms
Sergiy Nikolayev
@snikolayev
@tnghub3 no, it doesn’t
tnghub3
@tnghub3
Any recommendations on how to do it with nrules?
larryPlayabl
@larryPlayabl

@snikolayev I'm curious to experiment a bit with some resource pooling to try to reduce allocations in NRules. I'd love to hear your thoughts on it when you have a moment.

One low-hanging-fruit spot I'd like to try is Tuple reuse. It's very clear where Tuples are constructed, and they could instead come from a pool of Tuples which can be reused. Any ideas to help me track down the point/points in the code where can a Tuple be safely returned to the pool for reuse?

Sergiy Nikolayev
@snikolayev
@tnghub3 I suggest you step back and look at the problem you are trying to solve. Decision table is just a tool; see if you can reframe your problem in a way that can be solved with predicate logic/production rules. If so, you can use NRules to encode that logic. If decision tables is the only way to solve your problem, I would look for an appropriate library/framework outside NRules that would allow you to do that.
Sergiy Nikolayev
@snikolayev
@larryPlayabl I think it's a great idea to explore resource pooling. But I don't think Tuples is a good candidate. Tuples in NRules are stateful and long lived. They are also small and not expensive to create. The candidates I'm thinking about are arrays of facts created during expression evaluation, which are allocated and then collected right away. Another candidate is fact enumerator in a tuple, which is created numerous times during fact propagation through the graph. Also, fact lists and tuple lists created during propagation are good candidates for pooling; though these are harder to tackle due to the recursive nature of propagation. One other idea I have is to convert fact propagation from recursive to iterative, at which point pooling fact/tuple lists should be much easier.
larryPlayabl
@larryPlayabl

@snikolayev Thanks for the pointers. Yes, it makes sense to start with the easiest options that offer the greatest optimization for amount of effort and risk. I'll openly admit that I'm not especially well versed in this kind of optimization, and would definitely want to keep you in the loop as I experiment to make sure I'm going in the right direction. I know there can be subtle details in the implementation that may make all the difference in how much allocation savings we're actually reaping.

The object arrays created in the LHSExpression Invoke methods look like they'd be pretty straightforward to pool. Do you believe that swapping these arrays out for List<object> that are cleared and repopulated each time would offer an allocation advantage?

If that won't help significantly, I could also imagine caching a Dictionary<ITuple, object[]> to be used by these LHSExpression methods. I believe this would work because the object[] will always be the same size for the Tuple. Obviously, this cache would need to be updated whenever a given tuple's facts change. That could either be done by iterating through them and updating them on each call to Invoke, or by checking for a "dirty" flag on the tuple which would indicate that the facts have changed since the last invoke, though that'd be more complex.

For the Tuple.Facts IEnumerable, I could give each Tuple a List<Fact> that's cleared and repopulated by walking the tuples on each call to get the IEnumerable. However, again, maybe there'd be a more efficient way to do that and only update the Facts list as needed. Think that'd be possible?

larryPlayabl
@larryPlayabl
Actually, I assume it's possible that the Facts property for a given Tuple will be called while another caller is already iterating through its facts. So that'd mean that clearing the same reusable list with each call wouldn't be safe.
Sergiy Nikolayev
@snikolayev
@larryPlayabl regarding LhsExpression.Invoke, swapping arrays with Lists will likely not change anything, as that's still an allocation (and same array behind the scenes). How about ArrayPool? The rub here is that this is not compatible with .net framework 4.5, so that would significantly change what frameworks NRules targets. Regarding Dictionary caching - hard to say w/o prototyping. My hunch is that stateful stuff that lives together with the Tuple is likely going to be not suitable for pooling. Regarding Tuple.Facts - materializing lists of facts will likely just make it worse. At least right now enumerator is ephemeral. But it is the enumerator itself that's instantiated over and over again, and so I was thinking about pooling the enumerators. It's a bit hard to have a good back and forth here on gitter. How about discussing it here: NRules/NRules#200 ?
larryPlayabl
@larryPlayabl
Sure - I've moved the conversation over to github.
Gurdeep Sira
@gurdeepsira
Hi all, a question... how hard would it be to provide a custom gui for nrules in a web app/
?
Ramanarayan1986
@Ramanarayan1986
Please give me a solution to save rule in xml or database
Sergiy Nikolayev
@snikolayev
@gurdeepsira regarding a custom web gui for NRules. It's such a broad question that it's impossible to answer. I'll just leave it at - I don't have any plans to build such a UI.
Sergiy Nikolayev
@snikolayev
@Ramanarayan1986 you cannot save rules in XML with NRules. There are two DSLs for NRules - C# DSL (https://github.com/NRules/NRules/wiki/Fluent-Rules-DSL) and NRules language (https://github.com/NRules/NRules.Language). I would seriously recommend against storing rules in the database, and suggest you compile the rules and deploy them as separate assemblies. If you firmly decided to do it, mostly your options are 1) store C# in the DB and compile on the fly with Roslyn (again, I would seriously recommend against this). 2) use NRules.Language and store textual rules in the DB or 3) write your own specialized DSL, that you would compile into NRules.RuleModel using the RuleBuilder (https://github.com/NRules/NRules/wiki/Rule-Builder).
Just remember - by doing any of this, instead of using Visual Studio or VS Code for writing C# you are basically giving up an IDE with intellisense, type safety and compile-time checks, debugger, build infrastructure. Also, storing rules in the DB likely means bypassing any SDLC, source control, CI/CD practices, unless you build some heavy process around this. At which point I'm not really sure what you are gaining. IMO
Gurdeep Sira
@gurdeepsira
@snikolayev I have come back to this. I am doing a booking app for a friend and I can write some rules for him anyway, as long as some parameters are added (e.g. number of bookings). So I take it saving rules in JSON is not an option either? I think the seperate assembly approach is better and then inject that (in my case, a microservice will use it).
Sergiy Nikolayev
@snikolayev
@gurdeepsira correct, JSON is not an option either. You can easily parameterize the rules by modeling those parameters as facts, and just matching both the parameters and the facts from your domain model.
yashad singh
@yashad.singh_gitlab
Dependency resolver not provided. To use rule dependencies set a dependency resolver on the rules session.
I am new to NRules and trying to use services in my rules. But it is throwing error "Dependency resolver not provided. To use rule dependencies set a dependency resolver on the rules session.".
my service variable always set to "null".