TL;DR - 如果您需要将注册的单例之一作为配置选项传入AddMvc
, Don't做我所做的并使用GetService
in the ConfigureServices
方法。跳到下面的编辑 2。
I found 这个答案 https://stackoverflow.com/a/48856563/2869344类似的问题,但答案不清楚服务注册的顺序,但它确实指出调用services.BuildServiceProvider()
构建一个新的容器,导致服务被重新注册。事后看来,还蛮有道理的……
EDIT:
我最初的解决办法是搬家services.BuildServiceProvider()
之前AddSingleton
注册,但事实证明这并不总是有效,正如佛陀好友指出的那样。
我刚刚看到这个问题 https://stackoverflow.com/questions/32459670/resolving-instances-with-asp-net-core-di这给出了正在发生的事情的更多细节。原来的注册并没有像我想象的那样被丢弃。我当时想的全错了。
Calling services.BuildServiceProvider()
确实构建了一个新的服务提供者/容器,但这与注册无关。该类实现IUserRepository
并且所有依赖服务都被实例化由新服务提供商 when sp.GetService<IUserRepository>()
被称为,但原来IServiceProvider
仍然存在,这就是应用程序的其余部分将使用的内容。
因此,当应用程序需要一个实例时IUserRepository
(因为它是注入的依赖项UsersController
,现在已被请求),IUserRepository
服务(及其依赖项,如果有的话)再次实例化,因为它现在位于原始IServiceProvider
.
上述问题的答案中的一条评论指出,您可以防止这种情况并使用IServiceProvider
, “通过从ConfigureServices
方法,以便这也将成为您的应用程序使用的容器”。我不知道你会如何做那么短存储一个指向它的类变量,所以它可以在Configure
通过设置的方法app.ApplicationServices
- 但这对我来说也不起作用,因为新的服务提供商缺少所有 MVC 服务。
有些人建议使用app.ApplicationServices
in Configure()
访问所需的服务,但这对我不起作用,因为我需要在ConfigureServices
如下:
//...
serviceProvider = services.BuildServiceProvider();
var userRepository = serviceProvider.GetService<IUserRepository>();
// Add framework services
services.AddMvc(
config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new RolesAuthorizationFilter(userRepository));
});
EDIT 2:
找到解决方案了!这篇精彩的帖子 https://andrewlock.net/accessing-services-when-configuring-mvcoptions-in-asp-net-core/描述了我想要实现的目标并提出了一个非常简洁的解决方案。
如果您需要在 MVC 配置选项中传递对已注册单例之一的引用,而不是尝试在内部实例化它ConfigureServices
,你可以简单地创建一个新类来实现IConfigureOptions<MvcOptions>
它可以接受注入的依赖项,将此类注册为单例 -其他一切都得到了照顾 - 太棒了!
Example:
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
services.AddLogging();
services.AddSingleton<IConfiguration>(Configuration);
services.AddSingleton<IDbFactory, DefaultDbFactory>();
services.AddSingleton<IUserRepository, UserRepository>();
services.AddSingleton<IEmailService, EmailService>();
services.AddSingleton<IHostedService, BackgroundService>();
services.AddSingleton<ISettingsRepository, SettingsRepository>();
services.AddSingleton(typeof(TokenManager));
// var sp = services.BuildServiceProvider(); // NO NEED for this after all
// var userRepository = sp.GetService<IUserRepository>();
services.AddMvc(); // No config options required any more
services.AddSingleton<IConfigureOptions<MvcOptions>, ConfigureMvcOptions>(); // Here be the magic...
// ...
}
然后创建新类:
public class ConfigureMvcOptions : IConfigureOptions<MvcOptions>
{
private readonly IUserRepository userRepository;
public ConfigureMvcOptions(IUserRepository userRepository)
{
this.userRepository = userRepository;
}
public void Configure(MvcOptions options)
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new RolesAuthorizationFilter(userRepository));
}
}
感谢 DI 的魔力,其他一切都迎刃而解。我的用户存储库单例及其依赖项仅实例化一次。