首页 > 解决方案 > 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;
}

由于我在作曲根,我认为这种方法是正确的。
无论如何感谢您的帮助

标签: c#asp.net-coredependency-injectionextension-methodsservice-locator

解决方案


这可以通过构建您自己的(使用过的)容器来获取您的注册服务来实现。您可以保留您的扩展方法,因为我们有一个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>().


推荐阅读