NRules: как организовать правила в модулях и включить / отключить другие правила

Я начинаю с NRules. Вот краткое описание некоторых ключевых требований для приложения, которое я создаю:

  1. Некоторые из правил определяют, следует ли применять группы других правил. Например (в псевдокоде): "когда тип vehicle это автомобиль, примените все правила, применяемые к автомобилям ".

  2. Некоторые правила будут определять, что конкретные правила должны быть исключены.

  3. Там может быть много типов vehicle; в идеале я не хочу загружать все правила для всех транспортных средств при запуске.

Это подразумевает, что правила должны быть организованы в модули, которые каким-то образом включаются / отключаются во время выполнения. Несколько решений пришло мне в голову:

  • Помечайте правила и используйте фильтры Повестки дня для фильтрации правил в зависимости от службы, которая используется для включения / отключения добавления правил в повестку дня. Я попробовал это, и это не совсем работает так, как мне бы хотелось, потому что двигатель не воспринимает изменение службы.

  • Добавьте правила, которые определяют, должны ли правила применяться, и используйте это в условии соответствия каждого правила. Это приводит к большому количеству повторяющегося кода в каждом правиле, что мне не очень нравится.

  • Загружать новые группы правил во время выполнения правил. Я не уверен, как это сделать, если это рекомендуется или должно работать.

Есть ли правильный способ сделать то, что я пытаюсь сделать?

1 ответ

Решение

На высшем уровне есть два способа контролировать, какие правила применяются (и даже рассматриваются):

  1. Загрузите все правила в сеанс и обработайте его в рамках логики правил
  2. Загружать группы правил в разные сеансы и иметь какие-то мета-правила, которые отправляют элемент управления в соответствующий поднабор правил.

Одним из упомянутых вами требований было даже не загружать ненужные правила. Если это действительно так, я думаю, что вы находитесь прямо во второй группе решений. Движок не имеет встроенных механизмов, облегчающих этот сценарий, поэтому вам придется создавать его самостоятельно. На мой взгляд, вам понадобится набор мета-правил, загруженных в их собственный сеанс, которые вычисляют критерии, которые вы потом будете использовать для загрузки определенных правил. Например, у вас будет правило, которое соответствует транспортному средству, если оно является автомобилем, и вставляет тег "автомобиль" в сеанс. Некоторые другие мета-правила могут вычислять больше тегов. Затем вы будете использовать вычисленные теги для загрузки дополнительных наборов правил, например, загрузить все правила, которые имеют любой из этих вычисленных тегов, и скомпилировать их в отдельный сеанс, а затем выполнить ваши факты в соответствии с этими загруженными правилами. Правила должны быть скомпилированы и загружены в новый сеанс, потому что после компиляции фабрика сеансов является неизменной, поэтому новые правила не могут быть добавлены в нее.

Если вы можете смягчить требование не загружать все правила, у вас есть еще несколько вариантов. Вы уже затронули некоторые из вариантов в своем вопросе, но я думаю, что вам лучше всего использовать прямую цепочку для достижения того, чего вы хотите. Я лично думаю, что это лучше, чем пытаться динамически загружать и компилировать правила.

Например, у вас может быть правило, которое соответствует транспортному средству, если оно является автомобилем, и дает новый факт - автомобиль, который правила для конкретных автомобилей будут использовать позже:

Vehicle vehicle = null;
When()
    .Match(() => vehicle, v => v.VehicleType == VehicleType.Car);

Then()
    .Yield(_ => new Car(vehicle));

Тогда правила вашего автомобиля будут выглядеть так:

Car car = null;
When()
    .Match(() => car)
    //...

Ни одно из правил, относящихся к конкретному автомобилю, даже не будет оценено, пока не сработает ваше правило, определяющее факт автомобиля.

Другие вопросы по тегам