2

I'm trying to figure out the BRE NRules and got some examples working but having a hard time to match a collection.

IEnumerable<Order> orders = null;

When()
    .Match<IEnumerable<Order>>(o => o.Where(c => c.Cancelled).Count() >= 3)
    .Collect<Order>(() => orders, o => o.Cancelled);

Then()
    .Do(ctx => orders.ToList().ForEach(o => o.DoSomething()));

Basically what I want is if there are 3 orders cancelled then do some action. But I can't seem get a match on a collection, single variables do work.

The program:

var order3 = new Order(123458, customer, 2, 20.0);
var order4 = new Order(123459, customer, 1, 10.0);
var order5 = new Order(123460, customer, 1, 11.0);

order3.Cancelled = true;
order4.Cancelled = true;
order5.Cancelled = true;

session.Insert(order3);
session.Insert(order4);
session.Insert(order5);

session.Fire();

What am I doing wrong here?

Jehof
  • 34,674
  • 10
  • 123
  • 155
Elger Mensonides
  • 6,930
  • 6
  • 46
  • 69

3 Answers3

5

With the 0.3.1 version of NRules, the following will activate the rule when you collected 3 or more canceled orders:

IEnumerable<Order> orders = null;

When()
    .Collect<Order>(() => orders, o => o.Cancelled)
        .Where(x => x.Count() >= 3);
Then()
    .Do(ctx => orders.ToList().ForEach(o => o.DoSomething()));

Update:

For posterity, starting with version 0.4.x the right syntax is to use reactive LINQ. Matching a collection will look like this:

IEnumerable<Order> orders = null;
When()
    .Query(() => orders, q => q
        .Match<Order>(o => o.Cancelled)
        .Collect()
        .Where(x => x.Count() >= 3));
Then()
    .Do(ctx => DoSomething(orders));
Sergiy Nikolayev
  • 702
  • 4
  • 13
  • What is the purpose of the 'Where' clause after the 'Collect' clause? My code compiles and runs without the 'Where' (specifically, I'm using 'Where(c => c.Any())' that I found in the examples). I'm guessing the 'Where' clause will stop the evaluation when there are no results, making the rule more efficient. Is that right? – Quark Soup Nov 18 '22 at 12:36
  • 1
    @Quarkly you got it right. Collect can match an empty collection, if there are no matching facts. So, if you want your rule to fire only if there are some matching facts, you use Where(c => c. Any()). It's not really for efficiency, but more of a functional statement, depending on what you want your rules to do. But as you indicated, the Where clause is optional, and if you omit it, Collect will yield an empty collection if there are no matching facts, and the rule will still fire. – Sergiy Nikolayev Nov 20 '22 at 22:36
  • I assume that GroupBy doesn't need the same kind of construct. If there are no matching elements, you can't have a matching key. Is that the correct way to think about the GroupBy matching? – Quark Soup Nov 24 '22 at 14:22
  • 1
    @Quarkly yes, exactly, GroupBy only propagates non-empty groups – Sergiy Nikolayev Nov 24 '22 at 22:19
2

In your example, it should be pretty straightforward

IEnumerable<Order> orders = null;

When()
    .Collect<Order>(() => orders, o => o.Cancelled == true);

Then()
    .Do(ctx => orders.ToList().ForEach(o => o.DoSomething()));

I think the important part is the o.Cancelled alone without the == true. I know this sound wack, but somehow the property evaluation alone that is not an expression (i.e. just the property) is not well supported in NRules.

I ran into this problem and when I added the == true everything fell into place.

Kevin Moore
  • 179
  • 7
-1

How to join Multiple Collection based on some expression like

  IEnumerable<RawMsp> rawMsps = null;
        IEnumerable<AsmMasterView> asmMasterViews = null;
        IEnumerable<AsmInvestor> asmInvestors = null;            

        When()
            .Match<AsmInvestor>(() => rawMsps)
            .Match<AsmInvestor>(() => asmInvestor, i => i.InvestorId.ToString() == rawMsp.INVESTOR_CODE)
            .Match<AsmMasterView>(() => asmMasterView, x => x.ChildAssumptionHistId == asmInvestor.AssumptionHistId);    

Match is applicable individual object , Not sure apply equals of Enumerable Objects

Gopi
  • 1,425
  • 2
  • 10
  • 6