gpt4 book ai didi

c# - 如果不实例化两次,就不能在 ConfigureServices 中使用已注册的单子(monad)

转载 作者:行者123 更新时间:2023-11-30 15:15:46 25 4
gpt4 key购买 nike

我有一个 .Net Core 项目,它注册了一些单例,如下所示:

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();
var userRepository = sp.GetService<IUserRepository>();
// ...
}

这是唯一注册这些类的地方,没有在任何地方创建其他实例,但是我注意到构造函数被调用了两次。这是为什么?

最佳答案

TL;DR - 如果您需要将注册的单例之一作为配置选项传递给 AddMvc不要做我做的事并使用GetServiceConfigureServices方法。跳到下面的编辑 2。

我找到了 this answer对于一个类似的问题,但关于服务注册的顺序的答案并不清楚,但它确实指出调用 services.BuildServiceProvider()构建一个新容器,使服务重新注册。事后看来有点道理......

编辑:

我最初的修复是移动 services.BuildServiceProvider()AddSingleton 之前注册,但事实证明这并不总是有效,正如 Buddha Buddy 指出的那样。

我刚看到 this question它提供了更多关于正在发生的事情的细节。原始注册并没有像我认为的那样被丢弃。我当时想的全错了。

调用 services.BuildServiceProvider()确实构建了一个新的服务提供者/容器,但这与注册无关。类实现 IUserRepositorysp.GetService<IUserRepository>() 时,所有相关服务都由新服务提供者实例化被调用,但是原来的IServiceProvider保留下来,这就是应用程序的其余部分将使用的内容。

所以当应用程序需要一个 IUserRepository 的实例时(比如因为它是 UsersController 的注入(inject)依赖项,现在已被请求),IUserRepository服务(及其依赖项,如果有的话)再次被实例化,因为它现在位于原始 IServiceProvider 之下.

上述问题的答案中的评论指出您可以防止这种情况并使用 IServiceProvider。 , “通过从 ConfigureServices 方法返回服务提供者实例,这样它也将成为您的应用程序使用的容器”。我不确定除了存储指向它的类变量之外你会怎么做,所以它可以在 Configure 中换出方法通过设置 app.ApplicationServices - 但这对我也不起作用,因为新的服务提供商缺少所有 MVC 服务。

有些人建议使用 app.ApplicationServicesConfigure()访问所需的服务,但这对我不起作用,因为我需要在 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));
});

编辑 2:

找到解决方案! This brilliant post描述了我试图实现的目标并提出了一个非常简洁的解决方案。

如果您需要将对其中一个已注册单例的引用作为 MVC 配置选项传递,而不是尝试在 ConfigureServices 中实例化它,您可以简单地创建一个实现 IConfigureOptions<MvcOptions> 的新类它可以采用注入(inject)的依赖项,将此类注册为单例 - 其他一切都已处理 - 太棒了!

例子:

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 的魔力,其他一切都得到了处理。我的用户存储库单例及其依赖项仅实例化一次。

关于c# - 如果不实例化两次,就不能在 ConfigureServices 中使用已注册的单子(monad),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51591072/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com