【问题标题】:NRules with Asp.Net Core scoped dependency could not be resolved无法解析具有 Asp.Net Core 范围依赖的 NRules
【发布时间】:2021-08-25 17:57:56
【问题描述】:

运行示例here 时出现以下错误。

Cannot resolve scoped service 'IPersonService' from root provider.

我想将服务IPersonService注入到规则MalePersonRule

请注意,同样的服务IPersonService 被注入到HomeController

我已将此 IPersonService 注册为 Startup.cs 类中的范围服务。

我使用了这个NRules.Integration.AspNetCore nuget package 并得到了错误。因此,为了了解更多信息,我将nuget from this git hub repo 的来源包含在我的solution in this folder 中。

如何在请求范围内注册服务,以便该服务在相同范围内也被注入到规则中?这可能吗?

我知道规则不能在请求范围内。该规则在应用程序启动时被实例化,然后在所有请求的剩余生命周期中使用相同的规则。我希望服务依赖关系在请求范围内,这意味着要为每个新请求重新实例化该服务。这可能吗?

但我猜the rule session 是为每个请求重新创建的。

到目前为止,我还不能令人满意地将 NRules 集成到 AspNetCore 项目中。我研究了这个NRules.Demo project 也是为了了解如何集成,但面临类似的问题。如果有任何展示如何集成的 aspnet 核心演示项目,请分享。

我错过了什么?

重现问题的working solution 在这里供大家参考。

 NRules.RuleRhsExpressionEvaluationException
   HResult=0x80131500
   Message=Failed to evaluate rule action
 (ctx, serviceProvider) => WriteLine(Convert(Convert(serviceProvider.GetService(NRulesWithAspNetCore.Services.IPersonService), IPersonService).Id, Object))
 NRulesWithAspNetCore.Rules.MalePersonRule
   Source=NRules
   StackTrace:
    at NRules.ActionExecutor.Execute(IExecutionContext executionContext, IActionContext actionContext)
    at NRules.Session.Fire(Int32 maxRulesNumber, CancellationToken cancellationToken)
    at NRules.Session.Fire(CancellationToken cancellationToken)
    at NRules.Session.Fire()
    at NRulesWithAspNetCore.Controllers.HomeController.Index() in D:\Trials\WorkFlow\ElsaBugReports\src\NR1003002NRulesAspNetCore\NR1003002NRulesAspNetCore\Controllers\HomeController.cs:line 46
    at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<<InvokeActionMethodAsync>g__Logged|12_1>d.MoveNext()
 
   This exception was originally thrown at this call stack:
     [External Code]
 
 Inner Exception 1:
 InvalidOperationException: Cannot resolve scoped service 'NRulesWithAspNetCore.Services.IPersonService' from root provider.
 

更新:

嗨,谢尔盖,

谢谢。您的解决方案正在运行。但我找到了另一种方法。

这是使用中间件。我得到了线索阅读this SO answer.

所以我创建了一个中间件如下。

public class NRulesMiddleWare
{
    private readonly RequestDelegate _next;

    public NRulesMiddleWare(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context, 
        ISessionFactory sessionFactory, IDependencyResolver dependencyResolver)
    {
        // Reference: https://stackoverflow.com/a/48591356/1977871

        // Note 1. sessionFactory object is singleton meaning, its created at the start of the application.
        // Then its the same instance is used throughout the applicatioin life cycle.
        // But then the dependency resolver needs to be assigned everytime a request arrives.
        // If dependencyResolver is assigned rigth in the beginning at the time of registration of 
        // sessionFactory, then its not working. I am Not sure why. But using this middleware, its working.

        // Note 2. Here the dependency resolver instance is assigned afresh for each request.

        sessionFactory.DependencyResolver = dependencyResolver;
        await _next.Invoke(context);
    }
}

注意上面的invoke方法对IDependencyResolver有依赖,所以需要注册。当然中间件本身也需要注册。

总之,3个变化

  1. 在解决方案中包含上述中间件。
  2. 注册它的启动类配置方法。 app.UseMiddleware();
  3. 最后,将 IDependencyResolver 注册到 ConfigureServices 中的服务集合中。 services.AddScoped();

您的方法使用 new 关键字来实例化解析器。在 DI 世界中,我在某处读到,这种更新是大罪。对象创建必须由 DI 容器(ServiceCollection)对象负责。所以我觉得中间件方法更好。

【问题讨论】:

  • 在您使用中间件的解决方案中,您将在每个请求上更改 ISessionFactory.DependencyResolver。 ISessionFactory 是一个单例服务,因此如果您同时处理多个请求,可能会出现意外行为。关于新的 DependencyResolver - 它是 DI 基础架构代码的一部分,所以真的没有问题。但如果你更愿意解决它 - 你可以。
  • 好的,明白你在说什么。请让我想想。

标签: asp.net-core nrules


【解决方案1】:

在您提供的演示项目中,IDependencyResolver 连接到具有根 IServiceProvider 的 NRules ISessionFactory。每个会话都会继承该根解析器,因此会出现错误,因为无法通过根容器解析作用域服务。

您可以将默认的根解析器保留在出厂级别。但是对会话注册代码进行以下更改,以设置一个特定于会话的IDependencyResolver,其范围为IServiceProvider。这解决了这个问题,因为现在通过与会话关联的范围提供程序解决了依赖关系。

之前:

private static IServiceCollection RegisterSession(this IServiceCollection services)
{
    return services.AddScoped<ISession>(c => services.BuildServiceProvider().GetRequiredService<ISessionFactory>().CreateSession());
}

之后:

private static IServiceCollection RegisterSession(this IServiceCollection services)
{
    return services.AddScoped<ISession>(c =>
    {
        var factory = services.BuildServiceProvider().GetRequiredService<ISessionFactory>();
        var session = factory.CreateSession();
        session.DependencyResolver = new AspNetCoreDependencyResolver(c);
        return session;
    });
}

由于此代码位于 https://github.com/cloudb0x/NRules.Integration.AspNetCore 项目中,您可以与该包的作者合作以贡献更改,或者您可以按照我描述的方式实现您自己的会话级 IDependencyResolver

【讨论】:

    猜你喜欢
    • 2018-12-24
    • 2019-07-02
    • 2021-08-01
    • 2018-06-07
    • 1970-01-01
    • 1970-01-01
    • 2020-06-05
    • 1970-01-01
    • 2023-04-01
    相关资源
    最近更新 更多