依赖注入:无类型服务

时间:2019-04-16 10:40:04

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

我试图在整个应用程序中分离关注点,我认为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);
    }
}

我的ConfigureServices:

        //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<Validator<Promotion>, PromotionValidator>();

问题,如何在不需要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);
        });

1 个答案:

答案 0 :(得分:3)

var scopeFactory = services
    .BuildServiceProvider()
    .GetRequiredService<IServiceScopeFactory>();

由此,您正在构建一个单独的服务提供商,并使用该服务提供商的服务来获得服务范围工厂。

因此,当您以后执行以下操作时:

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

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

并且由于您仅在{em>之后注册了Validator<Promotion>,因此您创建了单独的服务提供商,因此该服务提供商将不包含您的验证程序服务。

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

就您而言,例如,可以通过更改验证提供程序的注册来达到目标​​:

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本身作为范围服务,以便它可以安全地从服务提供者访问范围服务(而不必管理自己的服务范围),或者如果您确实需要将其作为单例,则还可以移动这种逻辑进入验证提供程序实现本身。