属性 ABP 中的 NRules 注入
Property Injection for NRules within ABP
我正在使用 ABP Boilerplate 6.0 并将 NRules 与我们的平台集成。
- 我能够使下面的规则生效,但问题是我无法在规则条件中使用注入的“_questionResponseRepository”,因为只有在满足规则条件后,才会解决依赖关系.
- 我想使用“_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 的规则匹配条件中使用外部信息(在本例中是来自数据库的关键字)。
- 将相应的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();
- 从规则引擎外部的存储库中检索关键字,并将它们作为事实插入到会话中。这实际上是首选,因为您可以更好地控制与外部数据的交互。此外,您可以将关键字放入具有高效查找性能的数据结构中(例如 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 或#2。
我正在使用 ABP Boilerplate 6.0 并将 NRules 与我们的平台集成。
- 我能够使下面的规则生效,但问题是我无法在规则条件中使用注入的“_questionResponseRepository”,因为只有在满足规则条件后,才会解决依赖关系.
- 我想使用“_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 的规则匹配条件中使用外部信息(在本例中是来自数据库的关键字)。
- 将相应的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();
- 从规则引擎外部的存储库中检索关键字,并将它们作为事实插入到会话中。这实际上是首选,因为您可以更好地控制与外部数据的交互。此外,您可以将关键字放入具有高效查找性能的数据结构中(例如 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 或#2。