添加 Mvc 服务后单独添加 Mvc 选项

Adding Mvc options separately after adding Mvc services

一个非常针对 Net Core 的问题。我想为 IServiceCollection 编写一个扩展方法,它将在应用程序中进行配置。

原因是,如果目前,某些组件(例如属性和控制器)位于单独的库中。所以,我想写一个扩展方法,它将负责每个库的配置。 配置必须独立于主应用程序配置。

这是当前代码(由于上述原因,我不喜欢它):

public void ConfigureServices(IServiceCollection services)
{
    //..
    services.AddMvc(options => {
            // is needed for the library "filters". 
            options.Filters.Add(typeof(ExternalValidationActionFilterAttribute));
        })
        // is needed for the library "controllers"
        .AddApplicationPart(typeof(ExternalController).Assembly)
        .AddControllersAsServices();

    //..
    services.AddSingleton<ExternalControllerConfiguration>(new ExternalControllerConfiguration());
    services.AddSingleton<ExternalValidationAttributeConfiguration>(new ExternalValidationAttributeConfiguration());
}

主应用程序的唯一方法是 AddMvc()。其余代码 特定于外部库 。我想避免将外部库特定逻辑与主应用程序逻辑混合。理想情况下,重构代码应如下所示:

public void ConfigureServices(IServiceCollection services)
{
    //..
    services.AddMvc();
    services.ConfigureExternalAttributes();
    services.ConfigureExternalControllers();
    //..
}

public static class ServiceCollectionExtensions
{
    public static void ConfigureExternalAttributes(this IServiceCollection services)
    {
        // TBD: check if Mvc services added
        //      if not - add new, with options
        //      if yes - add options to existing
        //          options.Filters.Add(typeof(ExternalValidationActionFilterAttribute));

        services.AddSingleton<ExternalValidationAttributeConfiguration>(new ExternalValidationAttributeConfiguration());
    }

    public static void ConfigureExternalControllers(this IServiceCollection services)
    {
        // TBD: check if Mvc services added
        //      if not - add new, with options
        //      if yes - add options to existing
        //          1. If 'part' not present already: .AddApplicationPart(typeof(ExternalController).Assembly)
        //          2. If 'AddControllersAsServices' not present already: .AddControllersAsServices();
        //             Else: skip

        services.AddSingleton<ExternalControllerConfiguration>(new ExternalControllerConfiguration());
    }
}

我最后的想法是去 git-hub,查看源代码并提出一些解决方案。但。有没有什么通用的方法可以达到这个结果?也许微软已经想到了这一点,所以我正在尝试重新实现轮子?

非常欢迎任何建议或代码示例。

对于应用程序部分,您可以使用 Microsoft.Extensions.DependencyInjection 包在单独的库中创建自定义扩展 class,正如 Kirk Larkin 所建议的那样。

  1. 在您的单独库中添加 "Microsoft.Extensions.DependencyInjection" 包。
Install-Package Microsoft.Extensions.DependencyInjection
  1. 在您的单独库中创建一个新的 class ExternalConfigurationExtensions.cs 并更新 class 命名空间,如下所述。
namespace Microsoft.Extensions.DependencyInjection
{
    public static class ExternalConfigurationExtensions
    {
        public static IMvcBuilder ConfigureExternalControllers(this IMvcBuilder builder)
        {
            if (builder == null)
                throw new ArgumentNullException(nameof(builder));

            builder.AddApplicationPart(typeof(ExternalController).Assembly);

            return builder;
        }
    }
}
  1. 更新您的 Startup.cs
services.AddMvc()
        .ConfigureExternalControllers();

在社区的帮助下,我能够完成修改。他们正在关注:

public static partial class MvcBuilderExtensions
{
    public static IMvcBuilder ConfigureExternalAttributes(this IMvcBuilder builder, ExternalValidationAttributeConfiguration attributeConfiguration = null)
    {
        if (builder == null)
            throw new ArgumentNullException(nameof(builder));

        builder.Services.Configure<MvcOptions>(o => {
            o.Filters.Add(typeof(ExternalValidationActionFilterAttribute));
        });

        // add default configuration
        if (attributeConfiguration == null)
            attributeConfiguration = new ExternalValidationAttributeConfiguration();

        builder.Services.AddSingleton<ExternalValidationAttributeConfiguration>(attributeConfiguration);

        return builder;
    }
}

    public static IMvcBuilder ConfigureExternalControllers(this IMvcBuilder builder, ExternalControllerConfiguration controllerConfiguration = null)
    {
        if (builder == null)
            throw new ArgumentNullException(nameof(builder));

        var externalControllerAssembly = typeof(ExternalController).Assembly;
        builder.AddApplicationPart(externalControllerAssembly);

        // Next part is optional. Is used to add controller as a service.
        // see: https://github.com/aspnet/AspNetCore
        // /Mvc/Mvc.Core/src/DependencyInjection/MvcCoreMvcBuilderExtensions.cs
        var feature = new ControllerFeature();
        builder.PartManager.PopulateFeature(feature);
        foreach (var controller in feature.Controllers
            .Where(w => w.Assembly == externalControllerAssembly)
            .Select(c => c.AsType()))
        {
            builder.Services.TryAddTransient(controller, controller);
        }

        // builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());

        // add default configuration
        if (controllerConfiguration == null)
            controllerConfiguration = new ExternalControllerConfiguration();

        builder.Services.AddSingleton<ExternalControllerConfiguration>(controllerConfiguration);

        return builder;
    }

我不得不深入研究 dotnet 核心源代码以将控制器添加为服务。否则一切都很顺利。谢谢大家!

P.S。在属性配置的情况下 - 可以使用不同类型的扩展,如下所示:

public static void ConfigureExternalAttributes(this IServiceCollection services)
{
    services.Configure<MvcOptions>(o => {
        o.Filters.Add(typeof(ExternalValidationActionFilterAttribute));
    });

    services.AddSingleton<ExternalValidationAttributeConfiguration>(new ExternalValidationAttributeConfiguration());
}