c# - C# ASP.NET Core 替代扩展方法来注册服务
问题描述
我的 AspNet Core 应用程序有问题,我有一个扩展方法来在外部库上注册配置,如示例所示:
public static AuthenticationBuilder AddSaml2PIdP(this AuthenticationBuilder builder, SamlSettings settings)
{
foreach (var idP in settings.IdPs)
{
builder.AddSaml2p(idP.Scheme, options =>
{
options.ServiceProviderOptions = SpOptionsHelper.GetSpOptions(settings);
....
}
}
return builder;
}
在 Startup 类中,在 ConfigureServices 我这样调用这个方法:
var samlSettings = _configuration.GetSection(SamlSettings.SamlSection).Get<SamlSettings>();
services.AddAuthentication()
.AddSaml2PIdP(samlSettings);
现在不幸的是我不得不进行一些更改,所以我需要注入依赖项才能在该类中执行其他操作,所以我不能再使用静态类或扩展方法,我应该这样做:
public class Saml2PExtension
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public Saml2PExtension(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public void AddSaml2PIdP()
{
using var scope = _serviceScopeFactory.CreateScope();
var settings = scope.ServiceProvider.GetRequiredService<SamlSettings>();
var metadataParser = scope.ServiceProvider.GetRequiredService<ISamlMetadataParser>();
var environment = scope.ServiceProvider.GetRequiredService<IWebHostEnvironment>();
var builder = scope.ServiceProvider.GetRequiredService<AuthenticationBuilder>();
foreach (var idP in settings.IdPs)
{
builder.AddSaml2p(idP.Scheme, options =>
{
options.ServiceProviderOptions = SpOptionsHelper.GetSpOptions(settings);
....
}
}
}
}
但是我应该如何在 ConfigureServices 方法中注册这个类并调用 configure 方法呢?
使用新的实现,我不是将 ServiceProvider 用作 ServiceLocator 反模式吗?
也欢迎可以以更优雅的方式解决问题的替代解决方案。
谢谢
** 编辑 **
最后,我选择保留第一个核心代码片段,在扩展方法中添加 IWebHostEnvironment 参数并手动实例化 SamlMetadataParser,如下所示:
public static AuthenticationBuilder AddSaml2PIdP(this AuthenticationBuilder builder, IWebHostEnvironment environment, SamlSettings settings)
{
var metadataParser = new SamlMetadataParser(new ...); // manually create object
foreach (var idP in settings.IdPs)
{
builder.AddSaml2p(idP.Scheme, options =>
{
options.ServiceProviderOptions = SpOptionsHelper.GetSpOptions(settings);
// Read metadata from file ...
var idPOptions = metadataParser.Pars(xml);
........
}
}
return builder;
}
由于我在作曲根,我认为这种方法是正确的。
无论如何感谢您的帮助
解决方案
这可以通过构建您自己的(使用过的)容器来获取您的注册服务来实现。您可以保留您的扩展方法,因为我们有一个IServiceCollection
通过 暴露的方法AuthenticationBuilder
,这非常方便:
public static AuthenticationBuilder AddSaml2PIdP(this AuthenticationBuilder builder,
SamlSettings settings)
{
using var scope = builder.Services.BuildServiceProvider().CreateScope();
//get the services
var settings = scope.ServiceProvider.GetRequiredService<SamlSettings>();
var metadataParser = scope.ServiceProvider.GetRequiredService<ISamlMetadataParser>();
var environment = scope.ServiceProvider.GetRequiredService<IWebHostEnvironment>();
var builder = scope.ServiceProvider.GetRequiredService<AuthenticationBuilder>();
//start using the services
foreach (var idP in settings.IdPs)
{
builder.AddSaml2p(idP.Scheme, options =>
{
options.ServiceProviderOptions = SpOptionsHelper.GetSpOptions(settings);
....
}
}
return builder;
}
请注意,这是可能的,但您需要关心您使用的服务所需的所有依赖项的注册顺序。实际上,您可以将 theAddAuthentication
放在末尾ConfigureServices
以确保它始终成功。但是如果有什么问题,就会抛出异常,我们可以相应地调整顺序。
更新:
关于可以IdpOptions
在 IdentityServer4 中使用的后期配置(供 OP 试用):
services.AddOptions<IdpOptions>()
.Configure((IdpOptions o,
SamlSettings settings,
ISamlMetadataParser metadataParser,
IWebHostEnvironment environment,
AuthenticationBuilder builder) => {
//use your services here to configure next ...
});
实际上,上述用途OptionsBuilder
支持一种最多注入 5 个依赖项的方法。您可以使用一个单独的类来实现IConfigureOptions<IdpOptions>
或IPostConfigureOptions<IdpOptions>
代替。然后只需将其配置为services.ConfigureOptions<TImplementation>()
.
推荐阅读
- mysql - WHERE 子句中具有无效数据的 MySQL 语句返回 0 行而不是失败
- python - SQLAlchemy-Marshmallow 如何将嵌套模式对象序列化为值列表
- javascript - 如何将“Sun, Oct 3”之类的日期字符串与当前日期进行比较?
- python - 获取可见破折号传单地图的边界框?
- reactjs - 使用两个查询从 React.useEffect 订阅 firestore(异步操作)
- python - 我的 Bitnami Django 网站显示的是 Bitnami 主页,而不是我的。如何改变它?
- javascript - 在 html 和 javascript 中导入环境变量
- azure - Azure B2C 如何设置具有最大会话限制的滚动会话?
- laravel - 使用 Laravel 的 foreignIdFor 方法并创建复合唯一键
- swift - 如何在 SwiftUI 中制作连体按钮?(苹果系统)