属性 ABP 中的 NRules 注入

Property Injection for NRules within ABP

我正在使用 ABP Boilerplate 6.0 并将 NRules 与我们的平台集成。

  1. 我能够使下面的规则生效,但问题是我无法在规则条件中使用注入的“_questionResponseRepository”,因为只有在满足规则条件后,才会解决依赖关系.
  2. 我想使用“_questionResponseRepository”从数据库中获取关键字列表并在规则匹配条件中使用这些词

调用代码

public WasteManagementManager(                      
IRepository<WasteBirthCertificateBlock, long> wasteBirthCertificateBlockRepository,
              IRepository<WasteBirthCertificateChain, long> wasteBirthCertificateChainRepository,
              ISession nRuleSession,
              ISessionFactory nRuleSessionFactory,
              ILogger log

              )
        {
            _wasteBirthCertificateBlockRepository = wasteBirthCertificateBlockRepository;
            _wasteBirthCertificateChainRepository = wasteBirthCertificateChainRepository;
            _nRuleSession = nRuleSession;
            _nRuleSessionFactory = nRuleSessionFactory;
            _log = log;
        }

public void Trigger()
{==> 
When I am in debug, _questionResponseRepository is NOT NUll. I'm trying inject it as a fact but that is not property injection .. I'm just trying one way or the other to get it working


    _nRuleSession.Insert(_questionResponseRepository); 
     _nRuleSession.Fire();
 }

规则代码

 namespace SagerSystems.AI.WasteManagements.NRules
    {
        [Name("Track Explosive Substances Rule")]
        public class TrackExplosiveSubstancesRule : Rule
        {
            private string[] _listOfExplosiveKeyWords = new string[] { "H3O", "N2" };
            public  IRepository<QuestionResponse, long> _questionResponseRepository { get; set; }
    
            public TrackExplosiveSubstancesRule()
            {
             **This does NOT work** (the value remains null) 
             Dependency()
            .Resolve(() => _questionResponseRepository);
            }
    
            public override void Define()
            {
             *This does work* but only after the rule fires) 
             Dependency()
              .Resolve(() => _questionResponseRepository);
           
             When()
              .Match(() => questionResponseDto, c => CheckKeyWord(c));
              
             Then()
              .Do(ctx => Console.WriteLine(“Test Condition Works”))
            }
        
          private bool CheckKeyWord(QuestionResponseDto questionResponseDto)
            {   
             ==> How do I user ‘questionResponseRepository’
                var isKeyWord= 
                _listOfExplosiveKeyWords.Any(c => questionResponseDto.QuestionText.Contains(c));
               return isKeyWord;
            }
        }
    }

有几种方法可以在 NRules 的规则匹配条件中使用外部信息(在本例中是来自数据库的关键字)。

  1. 将相应的repository/service注入到规则中。 有两种方法可以将依赖项注入规则。在规则 类 实例化期间,或在规则的 运行 时间通过 Dependency.Resolve DSL。正如您所指出的,由于 Dependency.Relsove 只能用在规则的右侧(操作),因此不适合此用例。但是您仍然可以在规则实例化期间将依赖项注入到规则中。 您需要在这里做的是向容器注册规则类型本身,实现一个 IRuleActivator 以通过该容器解析规则,并在加载规则时设置 RuleRepository.RuleActivator。如果存储库和规则都注册到同一个容器,则规则将被注入依赖项(您可以使用 属性 或构造函数注入,具体取决于您注册类型的方式)。然后你可以只使用表达式中的依赖关系。 我没有你所有的代码,所以假设它看起来像下面这样。在这里,我假设有一个 Keyword 实体的存储库,可用于从数据库中获取关键字。我也在使用构造函数注入,但同样适用于 属性 注入。
    public class RuleActivator : IRuleActivator
    {
        private readonly IIocResolver _iocResolver;

        public RuleActivator(IIocResolver iocResolver)
        {
            _iocResolver = iocResolver;
        }

        public IEnumerable<Rule> Activate(Type type)
        {
            yield return (Rule)_iocResolver.Resolve(type);
        }
    }

    public class RulesEngineModule : AbpModule
    {
        public override void Initialize()
        {
            //Find rule classes
            var scanner = new RuleTypeScanner();
            scanner.AssemblyOf<TrackExplosiveSubstancesRule>();
            var ruleTypes = scanner.GetRuleTypes();

            //Register rule classes with the container
            foreach (var ruleType in ruleTypes)
            {
                IocManager.Register(ruleType);
            }
            
            //Load rules into the repository; use a rule activator to resolve rules via the container
            var repository = new RuleRepository {Activator = new RuleActivator(IocManager)};
            repository.Load(x => x.From(s => s.Type(ruleTypes)));
            
            //Compile rules into the factory
            var factory = repository.Compile();

            //Register session factory instance
            IocManager.IocContainer.Register(
                Component.For<ISessionFactory>().Instance(factory));
            
            //Register session as a delegate that creates a new instance from a factory
            IocManager.IocContainer.Register(
                Component.For<ISession>().UsingFactoryMethod(k => k.Resolve<ISessionFactory>().CreateSession()).LifestyleTransient());
        }
    }

    [Name("Track Explosive Substances Rule")]
    public class TrackExplosiveSubstancesRule : Rule
    {
        private readonly IRepository<Keyword, long> _keywordRepository;
    
        public TrackExplosiveSubstancesRule(IRepository<Keyword, long> keywordRepository)
        {
            _keywordRepository = keywordRepository;
        }
    
        public override void Define()
        {
            QuestionResponseDto questionResponseDto = default;
            
            When()
                .Match(() => questionResponseDto, c => ContainsKeyword(c));

            Then()
                .Do(ctx => Console.WriteLine("Test Condition Works"));
        }
        
        private bool ContainsKeyword(QuestionResponseDto questionResponseDto)
        {
            var keywords = _keywordRepository.GetAll().ToList();
            var hasKeyWord = keywords.Any(keyword => questionResponseDto.QuestionText.Contains(keyword.Value));
            return hasKeyWord;
        }
    }

然后在您的 Trigger 方法中的某处或相应控制器中的某处:

    var dto = new QuestionResponseDto(...);
    _session.Insert(dto);
    _session.Fire();
  1. 从规则引擎外部的存储库中检索关键字,并将它们作为事实插入到会话中。这实际上是首选,因为您可以更好地控制与外部数据的交互。此外,您可以将关键字放入具有高效查找性能的数据结构中(例如 trie)。 在这种情况下,您不需要规则激活器或向容器注册规则,就像选项 #1 一样,因为所有输入都作为事实进入规则,所以实际上没有外部依赖性。 例如:
    public class KeywordSet
    {
        private readonly Keyword[] _keywords;

        public KeywordSet(IEnumerable<Keyword> keywords)
        {
            _keywords = keywords.ToArray();
        }
        
        public bool ContainsAny(string value)
        {
            return _keywords.Any(keyword => value.Contains(keyword.Value));
        }
    }
    
    [Name("Track Explosive Substances Rule")]
    public class TrackExplosiveSubstancesRule : Rule
    {
        public override void Define()
        {
            KeywordSet keywordSet = default;
            QuestionResponseDto questionResponseDto = default;
            
            When()
                .Match(() => keywordSet)
                .Match(() => questionResponseDto, c => keywordSet.ContainsAny(c.QuestionText));

            Then()
                .Do(ctx => Console.WriteLine("Test Condition Works"));
        }
    }

然后在您的 Trigger 方法中的某处或相应控制器中的某处:

    var keywords = _keywordRepository.GetAll().ToList();
    var keywordSet = new KeywordSet(keywords);
    _session.Insert(keywordSet);
    
    var dto = new QuestionResponseDto(...);
    _session.Insert(dto);
    _session.Fire();
  1. 您也可以将存储库本身作为事实插入,然后在规则中匹配它,但我不建议这样做,所以我会坚持选择#1 或#2。