我试图在整个应用程序中分离关注点,我认为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);
});
答案 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
本身作为范围服务,以便它可以安全地从服务提供者访问范围服务(而不必管理自己的服务范围),或者如果您确实需要将其作为单例,则还可以移动这种逻辑进入验证提供程序实现本身。