如果不实例化两次,则无法在ConfigureServices中使用注册的单例

2024-02-05

我有一个 .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, 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 的魔力,其他一切都迎刃而解。我的用户存储库单例及其依赖项仅实例化一次。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如果不实例化两次,则无法在ConfigureServices中使用注册的单例 的相关文章

  • 为什么相同的代码在同一台计算机上的执行时间可能不同?

    我是 C 编程新手 我编写了代码并希望获得它的运行时 这就是我所做的 每次运行代码时 我都会得到不同的运行时值 这样对吗 或者我的代码有问题吗 int main int argc char argv time t start end sta
  • 如何在 C++ 中的文件末尾添加数据?

    我已按照网上的说明进行操作 此代码应该将输入添加到文件 数据库 的末尾 但当我检查时 数据会覆盖现有数据 请帮忙 这是我的代码 int main string name string address string handphone cou
  • 用 C++ 进行服装建模 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在编写一些软件 最终会绘制一个人体框架 可以配置各种参数 并且计划是在假人身上放置某种衣服 我研究
  • 在 C# 中创建具有单独列的分隔文本

    我一直在尝试在 C 中创建一个制表符限制的文本文件 以便数据正确显示在单独的列中 Firstname Lastname Age John Smith 17 James Sawyer 31 我尝试过 t 字符 但我得到的只是 Firstnam
  • 使用Physics.Raycast 和Physics2D.Raycast 检测对象上的点击

    我的场景中有一个空的游戏对象 带有 2D 组件盒碰撞器 我将脚本附加到该游戏对象 void OnMouseDown Debug Log clic 但是当我点击我的游戏对象时 没有任何效果 你有什么想法 如何检测我的盒子碰撞器上的点击 使用光
  • C++中的类查找结构体数组

    我正在尝试创建一个结构数组 它将输入字符串链接到类 如下所示 struct string command CommandPath cPath cPathLookup set an alarm AlarmCommandPath send an
  • 存储来自其他程序的事件

    我想将其他应用程序的事件存储在我自己的应用程序中 事件示例 打开 最小化 Word 或打开文件时 这样的事可能吗 运行程序 http msdn microsoft com en us library ms813609 aspx and 打开
  • 获取 WPF 控件的所有附加事件处理程序

    我正在开发一个应用程序 在其中动态分配按钮的事件 现在的问题是 我希望获取按钮单击事件的所有事件 因为我希望删除以前的处理程序 我尝试将事件处理程序设置为 null 如下所示 Button Click null 但是我收到了一个无法分配 n
  • 关于在 Windows 上使用 WiFi Direct Api?

    我目前正在开发一个应用程序 我需要在其中创建链接 阅读 无线网络连接 在桌面应用程序 在 Windows 10 上 和平板电脑 Android 但无关紧要 之间 工作流程 按钮 gt 如果需要提升权限 gt 创建类似托管网络的 WiFi 网
  • 使用 JNI 从 Java 代码中检索 String 值的内存泄漏

    我使用 GetStringUTFChars 从使用 JNI 的 java 代码中检索字符串的值 并使用 ReleaseStringUTFChars 释放该字符串 当代码在 JRE 1 4 上运行时 不会出现内存泄漏 但如果相同的代码在 JR
  • 如何将整数转换为 void 指针?

    在 C 中使用线程时 我面临警告 警告 从不同大小的整数转换为指针 代码如下 include
  • C++:.bmp 到文件中的字节数组

    是的 我已经解决了与此相关的其他问题 但我发现它们没有太大帮助 他们提供了一些帮助 但我仍然有点困惑 所以这是我需要做的 我们有一个 132x65 的屏幕 我有一个 132x65 的 bmp 我想遍历 bmp 并将其分成小的 1x8 列以获
  • 等待线程完成

    private void button1 Click object sender EventArgs e for int i 0 i lt 15 i Thread nova new Thread Method nova Start list
  • (de)从 CSV 序列化为对象(或者最好是类型对象的列表)

    我是一名 C 程序员 试图学习 C 似乎有一些内置的对象序列化 但我在这里有点不知所措 我被要求将测试数据从 CSV 文件加载到对象集合中 CSV 比 xml 更受青睐 因为它更简单且更易于人类阅读 我们正在创建测试数据来运行单元测试 该集
  • Server.MapPath - 给定的物理路径,预期的虚拟路径

    我正在使用这行代码 var files Directory GetFiles Server MapPath E ftproot sales 在文件夹中查找文件 但是我收到错误消息说 给定物理路径但虚拟路径 预期的 我对在 C 中使用 Sys
  • 有没有办法强制显示工具提示?

    我有一个验证字段的方法 如果无法验证 该字段将被清除并标记为红色 我还希望在框上方弹出一个工具提示 并向用户显示该值无效的消息 有没有办法做到这一点 并且可以控制工具提示显示的时间 我怎样才能让它自己弹出而不是鼠标悬停时弹出 If the
  • 线程和 fork()。我该如何处理呢? [复制]

    这个问题在这里已经有答案了 可能的重复 多线程程序中的fork https stackoverflow com questions 1235516 fork in multi threaded program 如果我有一个使用 fork 的
  • memset 未填充数组

    u32 iterations 5 u32 ecx u32 malloc sizeof u32 iterations memset ecx 0xBAADF00D sizeof u32 iterations printf 8X n ecx 0
  • 防止在工厂方法之外实例化对象

    假设我有一个带有工厂方法的类 class A public static A newA Some code logging return new A 是否可以使用 a 来阻止此类对象的实例化new 那么工厂方法是创建对象实例的唯一方法吗 当
  • 如何正确使用 std::condition_variable?

    我很困惑conditions variables以及如何 安全 使用它们 在我的应用程序中 我有一个创建 gui 线程的类 但是当 gui 是由 gui 线程构造时 主线程需要等待 情况与下面的函数相同 主线程创建互斥体 锁和conditi

随机推荐