yiyungent / PluginCore

🔌 ASP.NET Core lightweight plugin framework | ASP.NET Core 轻量级 插件框架 - 一分钟集成 | Vue.js frontend | JavaScript SDK
https://yiyungent.github.io/PluginCore/
GNU Lesser General Public License v3.0
458 stars 87 forks source link

System.InvalidOperationException: 'Unable to resolve service for type 'Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager' while attempting to activate 'PluginCore.PluginControllerManager'.' #1

Open yiyungent opened 3 years ago

yiyungent commented 3 years ago

使用 环境变量 方式 指定承载启动程序集后,报错

System.InvalidOperationException: 'Unable to resolve service for type 'Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager' while attempting to activate 'PluginCore.PluginControllerManager'.'

发生在

PluginManager pluginManager = scope.ServiceProvider.GetService<PluginManager>();

原因分析

由于外部承载启动程序集 (PluginCore) 先注册服务 ConfigureServices(IServiceCollection services), 而在这其中 尝试了一次 获取 ApplicationPartManager,但此时主程序 还没有注册服务 ApplicationPartManager ,导致无法解析

yiyungent commented 3 years ago
namespace AspNetCore3_1
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, "PluginCore");
                    webBuilder.UseStartup<Startup>();
                });
    }
}

Program.cs 中指定承载启动程序集,然而无论 与 UseStartup<Startup>() 先后顺序如何,始终先执行 外部承载启动程序集

webBuilder.UseStartup<Startup>()
                        .UseSetting(WebHostDefaults.HostingStartupAssembliesKey, "PluginCore");

依旧不改变顺序

[assembly: HostingStartup(typeof(PluginCore.PluginCoreHostingStartup))]
namespace PluginCore
{
    /// <summary>
    /// https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/host/platform-specific-configuration?view=aspnetcore-5.0
    /// </summary>
    public class PluginCoreHostingStartup : IHostingStartup
    {
        public PluginCoreHostingStartup()
        {

        }

        public void Configure(IWebHostBuilder builder)
        {
            //builder.ConfigureAppConfiguration(config =>
            //{

            //});

            // 注意: 无论是通过 Program.cs 中 webBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, "PluginCore");
            // 还是 通过环境变量 指定承载启动程序集, 都是先执行 外部的承载启动程序集, 再执行主程序的 Startup.cs, 因此在这时, 有些 service 还没有注册

            // TODO: 不知道, 重复 Add, Use 会导致什么, 没有做防止重复
            builder.ConfigureServices(services =>
            {
                // fixed: https://github.com/yiyungent/PluginCore/issues/1
                // System.InvalidOperationException: 'Unable to resolve service for type 'Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager'
                // TODO: 不确定, 这样是否可行, 事实上之后主程序还会 Add 一次, 不知道是否会导致存在多个 实例
                // 失败: 不是一个实例, 导致无法改变 Controller
                //Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager applicationPartManager =
                //    new Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager();
                //services.AddSingleton<Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager>(applicationPartManager);

                services.AddPluginCore();
            });

            builder.Configure(app =>
            {
                app.UsePluginCore();
            });
        }
    }
}
yiyungent commented 3 years ago

源代码,

Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager 注册处

var partManager = new ApplicationPartManager();

partManager.ApplicationParts.Add(CompiledRazorAssemblyApplicationPartFactory.GetDefaultApplicationParts(viewsAssembly).Single());
var builder = services
                .AddSingleton<ILoggerFactory, NullLoggerFactory>()
                .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
                .AddSingleton<DiagnosticSource>(listener)
                .AddSingleton(listener)
                .AddSingleton<IWebHostEnvironment, BenchmarkHostingEnvironment>()
                .AddSingleton<ApplicationPartManager>(partManager)
                .AddScoped<BenchmarkViewExecutor>()
                .AddMvc();
yiyungent commented 3 years ago

外部先的问题就在于,有些service获取不到,因为是在主程序中注册的,你可能会说在外部注册,但有些必须单例,我试过了,如ApplicationPartManager,会变成两个

实际是先外部的注册服务 ( ConfigureServices(IServiceCollection services) ),再内部注册服务,再外部注册中间件 ( Configure(IApplicationBuilder app) ),再内部注册中间件

我需要在外部获取到主程序的服务,so,因此必须外部后

yiyungent commented 3 years ago

承载启动程序集

参考