Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
    Nico Sap
    @NicoJuicy

    I "think" i have some complex shippinglogic. The "ShippingRules" contains a combination of "CountryFiltering", "ZipCodeFiltering" and calculating Weight for shipping.
    The weight is calculated in another rule.

    But picking the correct ShippingRule and applying it on the order is currently uncertain.
    What i have currently is this ( logic of IsValidCountry, IsValidZipCodeLogic and IsWithinWeightRange is okay)

    ShippingRules have a "MaxWeight" and they have to be ordered according to the Weight. I did this in the ApplyShippingCost method.

    Would this be correct?

        public override void Define()
        {
            Shop.Models.Order order = null;
            IEnumerable<Shop.Models.OrderLine> orderLines = null;
            IEnumerable<Shop.Models.ShippingRule> shippingRules = null;
            Models.ShippingRule shippingRule= null;
            Shop.ComplexTypes.Address deliveryAddress = null;
    
            When()
                .Match<Shop.ComplexTypes.Address>(a => !string.IsNullOrEmpty(a.Country))
                .Query(() => shippingRules, sr => sr.Match<Shop.Models.ShippingRule>(
                     s => s.ShippingMethod.isActive,
                     s => IsValidCountry(s.CountryLogic, deliveryAddress.Country),
                     s => IsValidZipCodeLogic(s.ZipCodeLogic, deliveryAddress.ZipCode),
                     s => IsWithinWeightRange(s.MaxWeight, order.Totals.TotalWeight)
                    ).Collect());
    
            Then()
                .Do(ctx => ApplyShippingCost(order, shippingRules))
                .Do(ctx => ctx.Update(order));
           }
    
            private static void ApplyShippingCost(Shop.Models.Order order, IEnumerable<Shop.Models.ShippingRule> shippingRules)
         {
            shippingRules = shippingRules.OrderBy(dl => dl.MaxWeight);
            order.Totals.Shipping = shippingRules.FirstOrDefault().Cost;
         }
    larryPlayabl
    @larryPlayabl
    @snikolayev Thanks very much for your response regarding Linq optimization from August 18th. Sorry for my delay in responding. Thanks for explaining that a Linq optimization really won't help much with performance or allocation. That makes sense. We are definitely seeing that updating our facts causes some significant performance overhead in our realtime interactive application. Any optimizations would be greatly appreciated. Let me know if I can help by providing any profiling analysis of where we're seeing the allocations. For example, I could provide more images of our profiler, as I did for NRules/NRules#191.
    ItsDubC
    @ItsDubC
    anyone else ever run into an issue w/ Dependency().Resolve() not being able to resolve any services? they always end up being null. Even tried w/ @cloudb0x 's implementation and I get the same behavior :(
    ItsDubC
    @ItsDubC
    this is a .net core web api using the out-of-box DI implementation
    cloudb0x
    @cloudb0x
    are you sure your service is registered correctly @ItsDubC ? can you post your startup class?
    Im currently using it at work and seems to work fine
    also Can you show me how you use the package ?
    Sergiy Nikolayev
    @snikolayev
    @ItsDubC see this test for usage example of Dependency: https://github.com/NRules/NRules/blob/develop/src/NRules/Tests/NRules.IntegrationTests/OneFactRuleWithDependencyTest.cs
    As @cloudb0x said, an example would be helpful. Are you by any chance trying to use dependencies in the rule conditions (as opposed to in the actions)? Dependencies usage in conditions is not supported.
    @larryPlayabl I'm working on improving the performance of Rete graph. It's complicated, so it's taking time. I created this issue to track it: NRules/NRules#200
    Any profiles you might have would definitely be helpful, as they may provide me insight into how different usage scenarios compare. Please attach any information you have that you can share to that ticket.
    ItsDubC
    @ItsDubC

    @cloudb0x @snikolayev thanks for responding! I am using NRules in a .NET core 2.1.1 project and I believe @cloudb0x 's nuget package is built for 2.2, so I could not use it outright. However my code is pretty similar. Here are the pertinent methods from my Startup class:

            // This method gets called by the runtime. Use this method to add services to the container
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddNRules(AppDomain.CurrentDomain.GetAssemblies());
                services.AddCors();
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    
                // Add S3 to the ASP.NET Core dependency injection framework.
                services.AddAWSService<Amazon.S3.IAmazonS3>();
    
                services.UseCcl("http://test.com");
    
                services.AddTransient<IRuleService, RuleService>();
                services.AddSingleton<IFakeService, FakeService>();
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline
            public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
                else
                {
                    app.UseHsts();
                }
    
                app.UseNRules();
                app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
                app.UseHttpsRedirection();
                app.UseMvc();
            }

    I actually don't need the service I'm trying to resolve in the Define method of my rule, so it definitely is not being used in the rule conditions. I did try to resolve it in Define() as a test, and it still couldn't. Anyway here is how I'm trying to resolve my FakeService:

    public class ColorCellBorderGreenWhenCurrentDataMoreThanPreviousRule : Rule
        {
            public override void Define()
            {
                CellDefinition cd = null;
                AdcFact currentFact = null;
                AdcFact previousFact = null;
    
                IEnumerable<AdcFact> facts = null;
                IEnumerable<AdcFact> sortedFacts = null;
    
                When()
                    .Match<CellDefinition>(() => cd, c => c.Id == 2102)
                    .Query(() => facts, x => x
                        .Match<AdcFact>()
                        .Collect()
                    )
                    .Let(() => sortedFacts, () => facts.OrderByDescending(o => o.FlowDataEffDate))
                    .Let(() => currentFact, () => sortedFacts.FirstOrDefault())
                    .Let(() => previousFact, () => sortedFacts.LastOrDefault())
                    .Having(() => currentFact.FlowDataVolume > previousFact.FlowDataVolume);
    
                Then()
                    .Do(ctx => SetBorderColorToGreen(cd));
            }
    
            public void SetBorderColorToGreen(CellDefinition cd)
            {
                IFakeService service = null;
    
                Dependency()
                    .Resolve(() => service);
    
                service.DoTheThing();
            }
        }

    As you can see I have a IFakeService -> FakeService mapping setup and am calling AddNRules() in ConfigureServices(), as well as calling UseNRules() in Configure(). In SetBorderColorToGreen(), service never gets resolved to a FakeService. Am I missing something?

    @snikolayev thanks for the link to the dependency test. Nothing stands out to me as being different from my implementation though w/ regards to dependency resolution. Those tests do resolve the services in Define() and then pass them to the action method, but I couldn't get resolution to work even when I tried in Define().

    One thing I noticed while debugging is that the constructor for my dependency resolver does get called, but a breakpoint in Resolve() never gets hit at any point during the lifetime of my application.

    Thank you guys again for your help on this!

    Sergiy Nikolayev
    @snikolayev
    Move Dependency().Resolve(...) out of SetBorderColorToGreen and into the body of the Define method
    Look at the example unit test I referenced earlier
    Sergiy Nikolayev
    @snikolayev
    You will not hit a breakpoint in Resolve method, because it contains a lambda expression (as opposed to a lambda delegate), and you cannot put breakpoints inside expressions, as they are getting compiled to delegates at runtime. One thing to note here is that these expressions (in conditions, actions, as well as dependency resolution) are not really code that's being executed upon rule construction. That code is converted to a data structure and compiled at runtime into a computational graph. So, these variables, like service are just tokens used to stitch expressions together. By moving Dependency... stuff to Define method, it becomes part of the rule's definition, so the rules engine will call the dependency resolver upon action execution, and bind the resulting service instance to the service variable. By passing that variable to the SetBorderColorToGreen method you will instruct the engine to pass it to the compiled action delegate that it creates from your action expression. So, pull that dependency stuff out, and set a breakpoint inside SetBorderColorToGreen method, and you should see your service to be resolved from the container.
    cloudb0x
    @cloudb0x
    @ItsDubC My package targets .NET Standard - this means it shouldnt be an issue for you to consume it .
    @ItsDubC As @snikolayev mentioned the best thing do when debugging if things work is to put break point in the method that is called by the Do() in order to see if everything is working correctly
    What I generally do is resolve the service in the define and pass a reference of it to my method that needs it
    ItsDubC
    @ItsDubC
    You guys are the best! I did as you suggested and resolved the service in Define(), passed it to my action method, and it works. @snikolayev as you noted because the rule code is not being executed upon construction, the service doesn't appear to resolve properly in Define() but by the time the rule is executed and an action is taken, the service has been properly resolved in the action method. Interestingly enough, the breakpoint I had in Resolve() in my IDependencyResolver DOES get hit immediately after firing the session.
    @cloudb0x it is the dependencies on the 2.2.0 versions of the .NET Standard libraries that were giving me grief, as I'm locked into using 2.1.1 for now. Thank you both again!
    cloudb0x
    @cloudb0x
    @ItsDubC - Let me investigate downgrading it to provide some more backward compatibility
    ItsDubC
    @ItsDubC
    @cloudb0x for sure your current code will work with 2.1.1, not sure how far back you want to go for compatibility tho. Thank you again for your contribution!
    Kyle Grierson
    @grierson
    Should I put Side-Effectful (Save to database, Send Email) into the Do section of a Rule? I'm worried that the rule might get triggered multiple times before all rules have been resolved, in which case multiple records or emails will be sent.
    Currently I'm inserting new ad-hoc Rules into the Context/Session which have a 'Handle' method. Then Querying the session after all the rules have fired and running each 'Handle' on each fact in the queried Session.
    Sergiy Nikolayev
    @snikolayev
    @grierson it's really up to you to use the approach that best suits your use case. The approach you described with using command objects is prone to the same exact issue should a rule fire more times than desired, sine you would insert multiple command objects. You can obviously "dedupe" those command objects, but you can also structure your rules to prevent multiple rules firing in the first place (e.g. invalidating conditions inside the action, considering the use of agenda filters, or using Repeatability attribute). Also, the approach you described with command objects is only usable if actions don't affect the subsequent firing of other rules.
    shivam khatri
    @shivamkhatri

    Hello @snikolayev ,
    In the last example over here: https://github.com/NRules/NRules/blob/develop/samples/RuleBuilder/RuleBuilder/CustomRuleRepository.cs

    At line 120, var orGroup = builder.LeftHandSide().Group(GroupType.Or);

    Converting it to: var orGroup = builder.LeftHandSide().Group(GroupType.And);
    results in exception : {"Duplicate declarations. Declaration=customer"}

    Do we need to have same pattern builder for And conditions?

    larryPlayabl
    @larryPlayabl
    @snikolayev I just took some time to do a profiling snapshot of allocations during fact update. My summary and images are added here: NRules/NRules#200
    Please let me know if you have any questions!
    Kyle Grierson
    @grierson
    @snikolayev Thank you! Do have any examples of 'invalidating conditions inside the action'? I'm just concerned if I started adding rules that said 'When fact A doesn't exist Then Add Fact B'. Then having another Fact that says 'When Fact B Exists Then redact Fact A' creating a sort of circular dependencies.
    Sergiy Nikolayev
    @snikolayev
    @larryPlayabl thank you! This is very helpful
    @shivamkhatri the example of an OR group you are referring to has "customer" on both sides of the OR condition. If you change it to AND that no longer makes sense - you would either need to bind them to different variables or only have "customer" on one side of AND.
    Sergiy Nikolayev
    @snikolayev
    @grierson invalidating conditions inside actions simply means doing something in the action that would make conditions no longer match. For example: https://github.com/NRules/NRules/blob/develop/samples/SimpleRules/SimpleRules/Rules/PreferredCustomerDiscountRule.cs
    Here a discount is applied only if no discount has been applied yet.
    Cuneyt Askeroglu
    @mehdiaskeroglu_twitter
    Hi
    How can I get the current session back?
    larryPlayabl
    @larryPlayabl
    @mehdiaskeroglu_twitter Can you clarify what you mean by that? When you create a session, you should store a reference to it.
    larryPlayabl
    @larryPlayabl
    This page includes a good example of the full flow to create a session: https://github.com/NRules/NRules/wiki/Getting-Started
    larryPlayabl
    @larryPlayabl

    @snikolayev I'm experimenting with compiling and creating multiple sessions asynchronously in separate threads. In general, this is working well, but very infrequently I see a "Recursive type definition detected" exception during the process.

    The stack trace where I get the failed to build rule definition exception:

      at NRules.Fluent.RuleDefinitionFactory.Create (NRules.Fluent.Dsl.Rule rule) [0x0001b] in <685ed49952174053b3ab0832cc9b6e51>:0 
      at NRules.Fluent.RuleDefinitionFactory.Create (System.Collections.Generic.IEnumerable`1[T] rules) [0x00016] in <685ed49952174053b3ab0832cc9b6e51>:0 
      at NRules.Fluent.RuleLoadSpec.Load () [0x0001d] in <685ed49952174053b3ab0832cc9b6e51>:0 
      at NRules.Fluent.RuleRepository.Load (System.Action`1[T] specAction) [0x0002a] in <685ed49952174053b3ab0832cc9b6e51>:0

    The underlying LINQ exception itself:

    Recursive type definition detected: 
     at System.Linq.Expressions.Expression`1[TDelegate].Create (System.Linq.Expressions.Expression body, System.String name, System.Boolean tailCall, System.Collections.Generic.IReadOnlyList`1[T] parameters) [0x00025] in <fbb5ed17eb6e46c680000f8910ebb50c>:0

    Any thoughts? Should it be threadsafe to do this? Perhaps I need to load my rule repositories synchronously, but then it's safe to compile them asynchronously?

    Thanks!

    Sergiy Nikolayev
    @snikolayev
    @larryPlayabl I’ve never seen this exception before. What’s the actual exception type? None of that code uses any shared state, so it must be in the .net framework at the point of generating expressions during rule instantiation. When loading rules in parallel, are the rule sets being loaded overlapping or orthogonal?
    larryPlayabl
    @larryPlayabl

    @snikolayev Unfortunately, my log didn't include the type, but I believe it's a TypeLoadException

    The rule sets I'm loading are orthogonal, but they do reference many of the same types.

    As mentioned, it's quite rare. Perhaps 1 in 1,000 executions.
    Fatih Küçük
    @fkucuk
    Hello,
    I need somehelp about the "RightHandSide"
    I want to set a field, based on a rule
    Fatih Küçük
    @fkucuk
    but the righthandside only accepts Action
    larryPlayabl
    @larryPlayabl

    @fkucuk You can have a right hand side ("Then") clause call an Action that is a method. Within that method, you can set fields, etc. You just need to be sure to pass any local variables into the method doing the operations.

    See the PreferredCustomerDiscountRule.ApplyDiscount method here https://github.com/NRules/NRules/wiki/Getting-Started for a good example.

    This example is a bit complicated since it takes a collection of facts, not a single fact, but the same principle applies. Also, note that if you want to have the changes you make affect subsequent processing of the rules, you need to update the fact in the rule engine. You can see this in the call from the example:

    .Do(ctx => ctx.UpdateAll(orders));

    For a single fact, you can call:

    .Do(ctx => ctx.Update(myFact));
    Rubén Guzmán
    @neburnz
    Hi everyone, I'm using the next code to insert ~6K facts using something like that: context.InsertAll(pagos); but this generates this exception: System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown. Do you have any hint to deal with massive Fact insertion?
    larryPlayabl
    @larryPlayabl
    @neburnz My guess is that you need to optimize your rules to shrink the size of the Rete net's intermittent fact match storage. You can do this by writing rules such that your first clauses reduce the matches as much as possible. In other words, write your match clauses from most specific to least specific.
    larryPlayabl
    @larryPlayabl

    Here is a response @snikolayev gave on this topic last year. You can search the gitter to find it in context and see the surrounding conversation:

    I don't think I can tell you exactly what the problem you are experiencing is caused by, because it very much depends on your rules, but I can generally describe why that could be happening, which can help you diagnose it. At the most general level, Rete algorithm (which powers the execution of the rules) sacrifices memory for speed. The way it does it, is it stores partial matches in memory. So, if you have a very large partial match, particularly a Cartesian Product of a set of facts, even if it is filtered down later in the graph, you can blow up the memory. An example is, say you have a 1000 facts of one kind, and a 1000 facts of another kind, and then you join them without a specific join condition, you are essentially creating a partial match of 1M of pairs (aka Cartesian Product). Here is a more specific example of another NRules user having a similar problem: NRules/NRules#148

    Rubén Guzmán
    @neburnz
    Ok, I’ll check it out... @larryPlayabl
    cloudb0x
    @cloudb0x
    Hi Guys , Quick question - is it possible to get the CONDITION that caused the rule to activate ?
    Ideally the context should inform me of why it happened , lets say a property is true on the object that im running the rules against - I would like to know the property that made the rule fire
    cloudb0x
    @cloudb0x
    @snikolayev