Лучший способ добиться прямой цепочки в NRULES
Я хочу запустить правило на основе результата предыдущего правила. Как я могу достичь этой функциональности, используя прямую цепочку? Я не хочу создавать разные объекты класса для каждого правила, чтобы добиться прямой цепочки.
Здесь, в этом примере, объект InstantDiscount создается только для этого единственного правила для достижения прямого связывания.
public class PreferredCustomerDiscountRule : Rule
{
public override void Define()
{
Customer customer = null;
IEnumerable<Order> orders = null;
Double total = Double.NaN;
When()
.Match<Customer>(() => customer, c => c.IsPreferred)
.Query(() => orders, x => x
.Match<Order>(
o => o.Customer == customer,
o => o.IsOpen)
.Collect())
.Let(() => total, () => orders.Sum(x => x.Amount))
.Having(() => total > 1000);
Then()
.Yield(_ => new InstantDiscount(customer, total * 0.05));
}
}
public class PrintInstantDiscountRule : Rule
{
public override void Define()
{
InstantDiscount discount = null;
When()
.Match(() => discount);
Then()
.Do(_ => Console.WriteLine("Customer {0} has instant discount of {1}",
discount.Customer.Name, discount.Amount));
}
}
1 ответ
Прямое связывание - это процесс, в котором одно правило изменяет рабочую память механизма правил таким образом, чтобы активировать некоторые другие правила. Этого можно достичь, вставив новые факты в механизм правил (используя Yield или IContext.Insert в NRules) или изменив некоторые из существующих фактов (используя IContext.Update).
Вот оригинальный пример, переформулированный так, чтобы привязать скидку к факту "Заказчик", а затем обновить этот факт, чтобы получить прямое сцепление.
public class PreferredCustomerDiscountRule : Rule
{
public override void Define()
{
Customer customer = null;
IEnumerable<Order> orders = null;
Double total = Double.NaN;
When()
.Match<Customer>(() => customer, c => c.IsPreferred, c => !c.DiscountPercent.HasValue)
.Query(() => orders, x => x
.Match<Order>(
o => o.Customer == customer,
o => o.IsOpen)
.Collect())
.Let(() => total, () => orders.Sum(x => x.Amount))
.Having(() => total > 1000);
Then()
.Do(ctx => ApplyDiscount(customer, 0.05))
.Do(ctx => ctx.Update(customer));
}
private static void ApplyDiscount(Customer customer, double discount)
{
customer.DiscountPercent = discount;
}
}
public class DicsountNotificationRule : Rule
{
public override void Define()
{
Customer customer = null;
When()
.Match(() => customer, c => c.DiscountPercent.HasValue);
Then()
.Do(_ => Console.WriteLine("Customer {0} has instant discount of {1}%",
customer.Name, customer.DiscountPercent));
}
}
При прямом объединении в цепочку путем обновления существующих фактов необходимо позаботиться о том, чтобы повторно не активировать правило, обновляющее факт, во избежание нежелательной рекурсии. Существует несколько механизмов управления рекурсией в NRules:
- Запишите условия таким образом, чтобы обновление аннулировало условия правила (это то, что мы сделали в приведенном выше примере; после установки скидки правило больше не будет соответствовать)
- Используйте атрибут Repeatability в правиле, чтобы предотвратить повторный запуск
- Используйте фильтры повестки дня, чтобы активировать правило, только когда происходят определенные изменения в соответствующих фактах.
Два последних варианта описаны в документации NRules.