## Where communities thrive

• Join over 1.5M+ people
• Join over 100K+ communities
• Free without limits
##### Activity
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.
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".
@snikolayev I am working with an API on dot net core. And dependencies have already been injected in serviceContainer in Startup.cs file. So i have no idea how the dependency injection works separately with NRules...
Sergiy Nikolayev
@snikolayev
@yashad.singh_gitlab if you want to use rule dependencies (which are resolved at rules run time), you need to implement and supply an IDependencyResolver (see https://github.com/NRules/NRules/wiki/Rule-Dependencies). Another approach is to inject dependencies into rules classes at rules instantiation time. In this case you need to implement IRuleActivator (see https://github.com/NRules/NRules/wiki/Fluent-Rules-Loading#Rule-Activation). When you implement one or both of these interfaces, you can simply just forward resolution requests to your existing service container.
Another NRules user I believe took a stab at implementing an integration project between NRules and .net core container: https://github.com/cloudb0x/NRules.Integration.AspNetCore

@snikolayev Is it possible to consume scoped services in MyRule (: Rule)? Currently it only work with Singleton service ( services.AddSingleton<IServiceTest, ServiceTest>(); ).

When I tried scoped services ( services.AddScoped<IServiceTest, ServiceTest>(); ) it threw an error as shown in the image above.

Sergiy Nikolayev
@snikolayev
@yashad.singh_gitlab you cannot use scoped services with rule activation, because rules are instantiated once during the application lifetime. But you should be able to use them with the runtime rules dependencies, via IDependencyResolver. When implementing the resolver, forward the resolution requests to the scoped service provider, instead of the root service provider. This also means you would have to create a new session per scope, create a dependency resolver for that scope and set it on that session.
Thanks @snikolayev ... It worked. Thanks a lot.
@snikolayev How can I make this call awaitable in above code in the image? Is it possible?
Sergiy Nikolayev
@snikolayev
@yashad.singh_gitlab no, async actions are not supported
@snikolayev I am creating a dynamic Rule as shown in above image. But I did not find a way to load that rule in RuleRespository.
Sergiy Nikolayev
@snikolayev
@yashad.singh_gitlab if you are creating rules dynamically, you don't need RuleRepository, as that's only used with the rules defined using C# DSL. When using RuleBuilder, you are getting back IRuleDefinitions, and you can feed them directly to RuleCompiler.Compile method.
ByteCode2408
@ByteCode2408
Hi! I don't know if this has been discussed before, but I would like to know if the rule engine will be a correct fit in a Domain Driven Design project. Use case: I have a Payment domain object and a set of business rules: if Payment.Amount is less than x, then process the payment using IExternalGateway1, if Amount between x and y, then try IExternalGateway2 if available, else retry only once with IExternalGateway1, if Amount > y, try only IExternalGateway3
Sergiy Nikolayev
@snikolayev
Hi @ByteCode2408 - I think many of the concepts and principles of DDD are fairly universal whether you are using a rules engine or not: ubiquitous language, bounded contexts, core domain and generic subdomains, etc. I also think you should fully invest into a rich domain model, if you are subscribing to DDD. Where the rules engine will add a new spin, IMO, is how you encode the logic that under DDD normally goes into services. Essentially, the way I'm thinking about the relationship between the domain model and rules is in terms of the type of logic they are encoding. A domain model of course encodes the structure of the domain in a form of entities and their relations. A domain model usually also encodes fairly static slowly changing logic, like invariants imposed by the domain. Rules on the other hand encode volatile logic - policies - that change frequently, or logic external to the core domain. In your case, you would model the Payment domain using rich entities. But then a service, say a PaymentGateway as an example, may actually spin up an NRules Session (from previously compiled payment rules), insert the Payment fact into that session (along with other items in the payment aggregate, using DDD terminology) and have those rules dispatch the payment to the external gateway.
ByteCode2408
@ByteCode2408
Cool, that's what I was thinking, thank you for clarification!
Jaliya Udagedara
@jaliyaudagedara
@snikolayev, we have started using NRules and it's good stuff. Have a question, can I please know how we can Yield a new fact for Forward Chaining when creating rules dynamically.
Benoit Provost
@namtar1447
Hello @jaliyaudagedara, from my very limited experience, when you define your action Expression, nothing prevents you from using ctx.InsertLinked() which should behave like Yield in terms of retraction.
Benoit Provost
@namtar1447

I also happen to have a question, I'm working on a project that requires going through rules and at some point having several choices (2-30 but possibly one choice leads to more choices down the line). I would like to be able to let the engine start from the current state and "trying" multiple solutions. I'm trying to figure out a way a simple and hopefully efficient way to do this. Here's what I experimented with/considered so far:

1) Considered using LinkedFacts, and while that handles new facts retraction, updates to existing facts wouldn't be reverted. So make a log of updates and revert manually?
2) Deep-copy all facts, make choice, continue execution. Once run is over, retract all, insert all fact copies, make new choice, repeat.
3) Deep-copy all facts, halt current session, create new session for each choice, starting with the fact copies.

I have the impression I'm missing a clever way to use the framework to help me. Additionally, doing this in a way where I can leverage already calculated fact combinations would be great as there are combinations that would be repeated.

The reason I'm trying to use NRules for this is that new and updated facts tend to trigger a limited number of rules and fact changes. For reference the "scale" I'm working with is along the lines of ~50-200 facts and probably ~50-200 rules.

Thanks!

Pravinskamane
@Pravinskamane
Hi, Is there a way to configure the Rules in database, and pull them to load the rules. Also how can we do some logging from the rule methods .
4 replies
Pravinskamane
@Pravinskamane
Hi, Thanks. Is there a way in which we can return any value from the Rule definition to the place of session.Fire()?
Pravinskamane
@Pravinskamane
Is there a way to set ordering sequence on Rules. For example to execute rules in a particular sequence?
2 replies
Têng Ûi
@tingwei628
Hi @snikolayev , I wonder how to do rule dependency injection with Unity container.
3 replies
Pavel Moskalevich
@happanda
Hi there. Is there a way to write rules that react specifically to Fact creation vs Fact update? Lets say, Fact is a class with one bool variable. I'd like one Rule that will .Do something when such Fact is created, .Undo something when Fact is retracted. And a different rule to fire when this bool value changes only for already existing Facts. Is it possible?
2 replies
Pavel Moskalevich
@happanda

Thanks for the previous answer. Now I'm trying to figure out how to deal with ActionTrigger. Consider the following CODE
When I run it, I get the expected output:

Activated Baz
Reactivated Zab
Deactivated Zab

But if I remove Action with ActionTrigger.Reactivated, only the first line is printed. The engine doesn't react to retraction. Why is that happening?

2 replies
Maarten Louage
@mlouage
Hello, I'm a new to this powerful library! I have a question which I did not find the answer in the documentation: is it possible to inject a dependency into a rule? I have rules on my domain model like if Country is not in the list of allowed countries to travel to, set a warning. That list of allowed countries is managed through a service which calls an HTTP endpoint. How can I inject this service into the rule?
6 replies
Argiris Gerogiannis
@ageroh
Hi all,
Have anybody used NRules.Language before ?
@ageroh I haven't used it, but I am going to be trying it out this weekend :)
Charanpreet Singh
@charanpreetsinghda
Hi there - is there a way to skip following rules within a ruleset as soon as a rule in that ruleset failed?
6 replies
morisamon
@morisamon
Hey everyone, is there a way to add dependency and option to resolve by rule builder and not by class definition?
Best regards,
Thank you :)