搜索
房产
装修
汽车
婚嫁
健康
理财
旅游
美食
跳蚤
二手房
租房
招聘
二手车
教育
茶座
我要买房
买东西
装修家居
交友
职场
生活
网购
亲子
情感
龙城车友
找美食
谈婚论嫁
美女
兴趣
八卦
宠物
手机

避免在ASP.NET Core 3.0中为启动类注入服务

[复制链接]
查看: 55|回复: 0

1万

主题

1万

帖子

3万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
32619
发表于 2020-1-15 00:33 | 显示全部楼层 |阅读模式
避免在ASP.NET Core 3.0中为启动类注入服务  热点新闻 65831-20200114230135354-150501171

本篇是怎样升级到ASP.NET Core 3.0系列文章的第二篇。
在本篇博客中,我将描摹从ASP.NET Core 2.x利用升级到.NET Core 3.0必要做的一个点窜:你不在必要在Startup机关函数中注入办事了。
在ASP.NET Core 3.0中迁移到通用主机

在.NET Core 3.0中, ASP.NET Core 3.0的托管底子已经被重新筹划为通用主机,而不再与之并利用用。那末这对于那些正在利用ASP.NET Core 2.x斥天时用的斥地职员,这意味着什么呢?在现在这个阶段,我已经迁移了多个利用,到现在为止,齐全都希望顺遂。官方的迁移指导文档可以很好的指导你完成所需的步伐,是以,我剧烈倡议你读一下这篇文档。
在迁移进程中,我碰到的最多两个题目是:

  • ASP.NET Core 3.0中设备中心件的举荐方式是利用端点路由(Endpoint Routing)。
  • 通用主机不答应为Startup类注入办事
其中第一点,我之前已经讲授过了。端点路由(Endpoint Routing)是在ASP.NET Core 2.2中引入的,可是被限制只能在MVC中利用。在ASP.NET Core 3.0中,端点路由已经是举荐的终端中心件实现了,由于它供给了很多好处。其中最垂危的是,它答应中心件获得哪一个端点终极会被实行,而且可以检索有关这个端点的元数据(metadata)。例如,你可以为健康检查端点利用授权。
端点路由是在设备中心件次第时必要出格留意。我倡议你再升级你的利用前,先阅读一下官方迁移文档针对此处的说明,后续我将写一篇博客来先容怎样将终端中心件转换为端点路由。
第二点,是已经提到了的将办事注入Startup类,可是并没有获得富足的宣传。我不太肯定能否是由于这样做的人不多,还是在一些场景下,它很轻易打点。在本篇中,我将展现一些题目场景,并供给一些打点计划。
ASP.NET Core 2.x启动类中注入办事

在ASP.NET Core 2.x版本中,有一个不为人知的特征,就是你可以在Program.cs文件中设备你的依靠注入容器。畴前我已经利用这类方式来举行强典范选项,然后在设备依靠注入容器的此外残剩部分时利用这些设备。
下面我们来看一下ASP.NET Core 2.x的例子:
  1. public class Program{    public static void Main(string[] args)    {        CreateWebHostBuilder(args).Build().Run();    }    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>        WebHost.CreateDefaultBuilder(args)            .UseStartup()            .ConfigureSettings(); // 设购置事,后续将在Startup中利用}
复制代码
这里有没有留意到在CreateWebHostBuilder中挪用了一个ConfigureSettings()的方式?这是一个我用来设备利用强典范选项的扩大方式。例如,这个扩大方式大要看起来是这样的:
  1. public static class SettingsinstallerExtensions{    public static IWebHostBuilder ConfigureSettings(this IWebHostBuilder builder)    {        return builder.ConfigureServices((context, services) =>        {            var config = context.Configuration;            services.Configure(config.GetSection("ConnectionStrings"));            services.AddSingleton(                ctx => ctx.GetService().Value)        });    }}
复制代码
所以这里,ConfigureSettings()方式挪用了IWebHostBuilder实例的ConfigureServices()方式,设备了一些设备。由于这些办事会在Startup初始化之前被设备到依靠注入容器,所以在Startup类的机关函数中,这些以设备的办事是可以被注入的。
  1. public static class Startup{    public class Startup    {        public Startup(            IConfiguration configuration,             ConnectionStrings ConnectionStrings) // 注入预设购置事        {            Configuration = configuration;            ConnectionStrings = ConnectionStrings;        }        public IConfiguration Configuration { get; }        public ConnectionStrings ConnectionStrings { get; }        public void ConfigureServices(IServiceCollection services)        {            services.AddControllers();            // 利用设备中的毗连字符串            services.AddDbContext(options =>                options.UseSqlServer(ConnectionStrings.BloggingDatabase));        }        public void Configure(IApplicationBuilder app)        {        }    }}
复制代码
我发现,当我先要在ConfigureServices方式中利用强典范选项工具设备其他办事时,这类形式很是的有用。在我上面的例子中,ConnectionStrings工具是一个强典范工具,而且这个工具在步伐进入Startup之前,就已经举行非空考证。这并不是一种正规的底子技术,可是实时证实利用起来很是的随手。
PS: 怎样为ASP.NET Core的强典范选项工具增加考证
但是,假如切换到ASP.NET Core 3.0通用主机以后,你会发现这类实现方式在运转时会收到以下的毛病信息。
  1. Unhandled exception. System.InvalidOperationException: Unable to resolve service for type 'ExampleProject.ConnectionStrings' while attempting to activate 'ExampleProject.Startup'.   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider)   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)   at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.UseStartup(Type startupType, HostBuilderContext context, IServiceCollection services)   at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.c__DisplayClass12_0.b__0(HostBuilderContext context, IServiceCollection services)   at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()   at Microsoft.Extensions.Hosting.HostBuilder.Build()   at ExampleProject.Program.Main(String[] args) in C:\repos\ExampleProject\Program.cs:line 21
复制代码
这类方式在ASP.NET Core 3.0中已经不再支持了。你可以在Startup类的机关函数注入IHostEnvironment和IConfiguration, 可是仅此而已。至于原因原由,应当是之前的实现方式会带来一些题目,下面我将给大家具体描摹一下。
留意:假如你对峙在ASP.NET Core 3.0中利用IWebHostBuilder, 而不利用的通用主机的话,你仍然可以利用之前的实现方式。可是我剧烈倡议你不要这样做,并尽大要的实行迁移到通用主机的方式。
两个单例?

注入办事到Startup类的底子题目是,它会致使系统必要构建依靠注入容器两次。在我之前展现的例子中,ASP.NET Core晓得你必要一个ConnectionStrings工具,可是唯一晓得怎样构建该工具的方式是基于“部分”设备构建IServiceProvider(在之前的例子中,我们利用ConfigureSettings()扩大方式供给了这个“部分”设备)。
那末为什么这个会是一个题目呢?题目是这个ServiceProvider是一个临时的“根”ServiceProvider.它建立了办事并将办事注入到Startup中。然后,残剩的依靠注入容器设备将作为ConfigureServices方式的一部分运转,而且临时的ServiceProvider在这时就已经被抛弃了。然后一个新的ServiceProvider会被建立出来,在其中包含了利用步伐“完整”的设备。
这样,即使办事设备利用Singleton生命周期,也会被建立两次:

  • 当利用“部分”ServiceProvider时,建立了一次,并针对Startup举行了注入
  • 当利用"完整"ServiceProvider时,建立了一次
对于我的用例,强典范选项,这大如果无关垂危的。系统并不是只可以有一个设备实例,这只是一个更好的挑选。可是这并非总是如此。办事的这类“泄漏”似乎是变动通用主机行为的垂危原因原由 - 它让工具看起来更平安了。
那末假如我必要ConfigureServices内部的办事怎样办?

固然我们已经不能像畴前那样设购置事了,可是还是必要一种可以更换的方式来满足一些场景的必要!
其中最多见的场景是经过注入办事到Startup,针对Startup.ConfigureServices方式中注册的其他办事举行状态控制。例如,以下是一个很是底子的例子。
  1. public class Startup{    public Startup(IdentitySettings identitySettings)    {        IdentitySettings = identitySettings;    }    public IdentitySettings IdentitySettings { get; }    public void ConfigureServices(IServiceCollection services)    {        if(IdentitySettings.UseFakeIdentity)        {            services.AddScoped();        }        else        {            services.AddScoped();        }    }    public void Configure(IApplicationBuilder app)    {        // ...    }}
复制代码
这个例子中,代码经过检查注入的IdentitySettings工具中的布尔值属性,决议了IIdentityService接口利用哪个实现来注册:大要利用假办事,大要利用真办事。
经过将静态办事注册转换为工场函数的方式,可以使必要注入IdentitySetting工具的实现方式与通用主机兼容。例如:
  1. public class Startup{    public Startup(IConfiguration configuration)    {        Configuration = configuration;    }    public IConfiguration Configuration { get; }    public void ConfigureServices(IServiceCollection services)    {        // 为依靠注入容器,设备IdentitySetting        services.Configure(Configuration.GetSection("Identity"));         // 注册差此外实现        services.AddScoped();        services.AddScoped();        // 按照IdentitySetting设备,在运转时返回一个切确的实现        services.AddScoped(ctx =>         {            var identitySettings = ctx.GetRequiredService();            return identitySettings.UseFakeIdentity                ? ctx.GetRequiredService()                : ctx.GetRequiredService();            }        });    }    public void Configure(IApplicationBuilder app)    {        // ...    }}
复制代码
这个实现明显比之前的版本要复杂的多,可是最少可以兼容通用主机的方式。
现实上,假如仅必要一个强典范选项,那末这个方式就有点过甚了。相反的,这里我大要只会重新绑定一下设备:
  1. public class Startup{    public Startup(IConfiguration configuration)    {        Configuration = configuration;    }    public IConfiguration Configuration { get; }    public void ConfigureServices(IServiceCollection services)    {        // 为依靠注入容器,设备IdentitySetting        services.Configure(Configuration.GetSection("Identity"));         // 重新建立强典范选项工具,并绑定        var identitySettings = new IdentitySettings();        Configuration.GetSection("Identity").Bind(identitySettings)        // 按照条件设备切确的办事        if(identitySettings.UseFakeIdentity)        {            services.AddScoped();        }        else        {            services.AddScoped();        }    }    public void Configure(IApplicationBuilder app)    {        // ...    }}
复制代码
除此之外,假如仅仅只必要从设备文件中加载一个字符串,我大要底子不会利用强典范选项。这是.NET Core默许模板中拥堵设备ASP.NET Core身份系统的方式 - 间接经过IConfiguration实例检索毗连字符串。
  1. public class Startup{    public Startup(IConfiguration configuration)    {        Configuration = configuration;    }    public IConfiguration Configuration { get; }    public void ConfigureServices(IServiceCollection services)    {        // 针对依靠注入容器,设备ConnectionStrings        services.Configure(Configuration.GetSection("ConnectionStrings"));         // 间接获得设备,不利用强典范选项        var connectionString = Configuration["ConnectionString:BloggingDatabase"];        services.AddDbContext(options =>                options.UseSqlite(connectionString));    }    public void Configure(IApplicationBuilder app)    {        // ...    }}
复制代码
这个实现方式都不是最好的,可是他们都可以满足我们的需求,以及大部分的场景。假如你畴前不晓得Startup的办事注入特征,那末你必定利用了以上方式中的一种。
利用IConfigureOptions来对IdentityServer举行设备

此外一个利用注入设备的常见场景是设备IdentityServer的考证。
  1. public class Startup{    public Startup(IdentitySettings identitySettings)    {        IdentitySettings = identitySettings;    }    public IdentitySettings IdentitySettings { get; }    public void ConfigureServices(IServiceCollection services)    {        // 设备IdentityServer的考证方式        services            .AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)            .AddIdentityServerAuthentication(options =>            {                // 利用强典范选项来设备考证处置赏罚器                options.Authority = identitySettings.ServerFullPath;                options.ApiName = identitySettings.ApiName;            });    }    public void Configure(IApplicationBuilder app)    {        // ...    }}
复制代码
在这个例子中,IdentityServer实例的基当地址和API资本名都是经过强典范选项选项IdentitySettings设备的. 这类实现方式在.NET Core 3.0中已经不再适用了,所以我们必要一个可更换的计划。我们可以利用之条件到的方式 - 重新绑定强典范选项大要间接利用IConfiguration工具检索设备。
除此之外,第三种挑选是利用IConfigureOptions, 这是我经过检察AddIdentityServerAuthentication方式的底层代码发现的。
究竟证实,AddIdentityServerAuthentication()方式可以做一些差此外事变。首先,它设备了JWT Bearer考证,而且经过强典范选项指定了考证的方式。我们可以利用它来延长设备命名选项(named options), 改成利用IConfigureOptions实例。
IConfigureOptions接口答应你利用Service Provider中的其他依靠项延长设备强典范选项工具。例如,假如要设备我的TestSettings办事时,我必要挪用TestService类中的一个方式,我可以建立一个IConfigureOptions工具实例,代码以下:
  1. public class MyTestSettingsConfigureOptions : IConfigureOptions{    private readonly TestService _testService;    public MyTestSettingsConfigureOptions(TestService testService)    {        _testService = testService;    }    public void Configure(TestSettings options)    {        options.MyTestValue = _testService.GetValue();    }}
复制代码
TestService和IConfigureOptions都是在Startup.ConfigureServices方式中同时设备的。
  1. public void ConfigureServices(IServiceCollection services){    services.AddScoped();    services.ConfigureOptions();}
复制代码
这里最垂危的一点是,你可以利用标准的机关函数依靠注入一个IOptions工具。这里不再必要在ConfigureServices方式中“部分构建”Service Provider, 即可设备TestSettings. 相反的,我们注册了设备TestSettings的意图,可是实在的设备会被推延到设备工具被利用的时候。
那末这对于我们设备IdentityServer,有什么帮助呢?
AddIdentityServerAuthentication利用了强典范选项的一种变体,我们称之为命名选项(named options). 这类方式在考证设备的时候非经常见,就像我们上面的例子一样。
简而言之,你可以利用IConfigureOptions方式将考证处置赏罚步伐利用的命名选项IdentityServerAuthenticationOptions的设备延长。是以,你可以建立一个将IdentitySettings作为机关参数的ConfigureIdentityServerOptions工具。
  1. public class ConfigureIdentityServerOptions : IConfigureNamedOptions{    readonly IdentitySettings _identitySettings;    public ConfigureIdentityServerOptions(IdentitySettings identitySettings)    {        _identitySettings = identitySettings;        _hostingEnvironment = hostingEnvironment;    }    public void Configure(string name, IdentityServerAuthenticationOptions options)    {         // Only configure the options if this is the correct instance        if (name == IdentityServerAuthenticationDefaults.AuthenticationScheme)        {            // 利用强典范IdentitySettings工具中的值            options.Authority = _identitySettings.ServerFullPath;             options.ApiName = _identitySettings.ApiName;        }    }    // This won't be called, but is required for the IConfigureNamedOptions interface    public void Configure(IdentityServerAuthenticationOptions options) => Configure(Options.DefaultName, options);}
复制代码
在Startup.cs文件中,你必要设备强典范IdentitySettings工具,增加所需的IdentityServer办事,并注册ConfigureIdentityServerOptions类,以便当必要时,它可以设备IdentityServerAuthenticationOptions.
  1. public void ConfigureServices(IServiceCollection services){    // 设备强典范IdentitySettings选项    services.Configure(Configuration.GetSection("Identity"));    // 设备IdentityServer考证方式    services        .AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)        .AddIdentityServerAuthentication();    // 增加其他设备    services.ConfigureOptions();}
复制代码
这里,我们无需向Startup类中注入任何内容,可是你仍然可以获得强典范选项的好处。所以这里我们获得一个双赢的结果。
总结

在本文中,我描摹了升级到ASP.NET Core 3.0时,可以必要对Startup 类举行的一些点窜。我经过在Startup类中注入办事,描摹了ASP.NET Core 2.x中的题目,以及怎样在ASP.NET Core 3.0中移除这个功用。末端我展现了,当必要这类实现方式的时候改怎样去做。

免责声明:假如加害了您的权益,请联系站长,我们会实时删除侵权内容,感谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Copyright © 2006-2014 全椒百姓网-全椒知名**,发布及时新鲜的全椒新闻资讯 生活信息 版权所有 法律顾问:高律师 客服电话:0791-88289918
技术支持:迪恩网络科技公司  Powered by Discuz! X3.2
快速回复 返回顶部 返回列表