-2

在这里运行示例时出现以下错误。

Cannot resolve scoped service 'IPersonService' from root provider.

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

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

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

我使用了这个NRules.Integration.AspNetCore nuget 包并得到了错误。因此,为了了解更多信息,我将这个 git hub repo 中的那个 nuget的来源包含在我的解决方案中的这个文件夹中。

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

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

但我猜规则会话是为每个请求重新创建的。

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

我错过了什么?

重现该问题的工作解决方案在这里供您参考

 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.
 

更新:

嗨,谢尔盖,

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

这是使用中间件。我得到了阅读这个 SO 答案的线索。

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

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. 最后,使用 ConfigureServices 中的服务集合注册 IDependencyResolver。services.AddScoped<IDependencyResolver, AspNetCoreDependencyResolver>();

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

4

1 回答 1

1

在您提供的演示项目中,使用 rootIDependencyResolver连接到 NRules 。每个会话都继承该根解析器,因此错误,因为无法通过根容器解析范围服务。ISessionFactoryIServiceProvider

您可以将默认根解析器保留在工厂级别。但是对会话注册代码进行以下更改,以设置一个特定 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.AspNetCoreIDependencyResolver项目中,您可以与该包的作者合作以贡献更改,或者您可以按照我描述的方式实现自己的会话级别.

于 2021-06-26T21:57:25.190 回答