【问题标题】:Dependency Injection: No Service for type依赖注入:没有服务类型
【发布时间】:2019-09-06 10:36:41
【问题描述】:

我试图在我的应用程序中分离关注点,我认为 Steven 的这个答案非常适合我的场景,但我试图删除 ninject 元素: Validation: How to inject A Model State wrapper with Ninject?

我已经从答案中添加了所有必需的类。

我有我的服务:

public class PromotionService {
    private readonly IValidationProvider validationProvider;

    public PromotionService(IValidationProvider validationProvider) {
        this.validationProvider = validationProvider;
    }

    public void CreatePromotion(string promoName) {
        //build the model
        Promotion promo = new Promotion() {
            //Name = promoName
        };

        //validate and throw validation exception
        validationProvider.Validate(promo);
    }
}

我的配置服务:

        //Register Business Logic services
        services.AddScoped<PromotionService>();

        //get scope factory
        var scopeFactory = services
                .BuildServiceProvider()
                .GetRequiredService<IServiceScopeFactory>();

        //https://stackoverflow.com/questions/4776396/validation-how-to-inject-a-model-state-wrapper-with-ninject
        Func<Type, IValidator> validatorFactory = type =>
        {
            var valType = typeof(Validator<>).MakeGenericType(type);
            return (IValidator)scopeFactory.CreateScope().ServiceProvider.GetRequiredService(valType);
        };

        services.AddSingleton<IValidationProvider>(x => new ValidationProvider(validatorFactory));

        services.AddScoped<Validator<Promotion>, PromotionValidator>();

问题调用validationProvider.Validate(promo);时出现异常

InvalidOperationException: No service for type 'VepoPortal.Services.Validators.Validator`1[VepoCustomerDatabase.Models.Promotion]' has been registered.

但是我在这里注册了:services.AddScoped&lt;Validator&lt;Promotion&gt;, PromotionValidator&gt;();

问题如何在不需要 Ninject 的情况下解决此问题?

编辑:用实际代码修复:

        services.AddScoped<Validator<Promotion>, PromotionValidator>();

        services.AddScoped<IValidationProvider>(sp => {
            Func<Type, IValidator> validatorFactory = type => {
                var valType = typeof(Validator<>).MakeGenericType(type);
                return (IValidator)sp.GetRequiredService(valType);
            };
            return new ValidationProvider(validatorFactory);
        });

【问题讨论】:

    标签: generics asp.net-core dependency-injection


    【解决方案1】:
    var scopeFactory = services
        .BuildServiceProvider()
        .GetRequiredService<IServiceScopeFactory>();
    

    这样,您正在构建一个独立服务提供者,并使用该服务提供者的服务来获得一个服务范围工厂。

    所以当您稍后执行以下操作时:

    return (IValidator)scopeFactory.CreateScope().ServiceProvider.GetRequiredService(valType);
    

    您正在使用那个单独的服务提供商。当您从该服务提供商处解析服务时,这些服务将与您的应用程序在其中运行的服务完全分开。

    而且由于您仅在创建了该单独的服务提供者之后才注册Validator&lt;Promotion&gt;,因此该服务提供者将不包括您的验证器服务。

    在同一个应用程序中拥有多个服务提供商通常不是一个好主意。它只会导致服务具有多个生命周期,例如每个服务提供者存在一次单例,当您与其他提供者的服务交互时会遇到问题(就像现在一样)。相反,您应该尝试仅使用一个服务提供商来解决您的问题。

    在您的情况下,您可以通过更改验证提供商的注册来实现:

    services.AddSingleton<IValidationProvider>(sp =>
    {
        Func<Type, IValidator> validatorFactory = type =>
        {
            var valType = typeof(Validator<>).MakeGenericType(type);
            return sp.GetRequiredService(valType);
        };
    
        return new ValidationProvider(validatorFactory);
    });
    

    传递给工厂的sp 是服务提供商,一个正确的。因此,您可以直接使用它来解析初始化提供程序所需的服务。

    您会注意到我没有在validatorFactory 内创建新的服务范围。这是因为当您使用服务范围时,您实际上应该始终在使用后处理它们。如果您只是创建它们并从中解析服务,那么服务范围将保持打开状态,您以后可能会遇到问题。

    在您的情况下,您无法正确处理服务范围,因为您需要返回已解析的对象并希望之后使用它。所以这意味着在这里使用服务范围不是一个好主意。您还应该考虑您是否真的需要一个服务范围:ASP.NET Core 已经为每个请求创建了一个服务范围,因此如果您确实需要一个范围依赖项,那么重用该范围是有意义的。

    不幸的是,这也意味着您对ValidationProvider 的实现与此并不真正兼容。您可以将ValidationProvider 本身设置为范围服务,以便它可以安全地从服务提供者访问范围服务(无需管理自己的服务范围),或者如果您确实需要它成为单例,您也可以移动此逻辑进入验证提供程序实现本身。

    【讨论】:

    • 谢谢,我非常感谢您的解释以及修复!我已经添加了上面的代码(你错过了验证提供者的回报,我注入了你所说的范围)
    猜你喜欢
    • 1970-01-01
    • 2020-09-09
    • 1970-01-01
    • 1970-01-01
    • 2021-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多