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; 
}

请注意,这是可能的,但是您需要关心您使用的服务所需的所有依赖项的注册顺序。实际上,您可以将 AddAuthentication 放在 ConfigureServices 的末尾以确保它始终成功。但是如果有什么问题,就会抛出异常,我们可以相应地调整顺序。

更新

关于可能与 IdentityServer4 中的 IdpOptions 一起使用的后配置(供 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>() 对其进行配置。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...