【问题标题】:Access IHost across application classes globally在全球范围内跨应用程序类访问 IHost
【发布时间】:2022-04-23 01:52:51
【问题描述】:

以下是 CreateHostBuilder 的示例代码。 Asp.net 核心主机负责通过构造函数和中间件解决依赖关系。

如果我们想为我们的自定义类解决它,它没有通过控制器或主方法调用,我们如何跨应用程序获取主机的实例。 是将其存储为静态变量的好方法还是有更好的方法?

public class Program
{
    public static async Task Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();

        using (var serviceScope = host.Services.CreateScope())
        {
            var services = serviceScope.ServiceProvider;

            try
            {
                var serviceContext = services.GetRequiredService<MyScopedService>();
                // Use the context here
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred.");
            }
        }

        await host.RunAsync();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

【问题讨论】:

  • .Net Core 会在你所有的类中注入它,你可以通过类构造函数得到它,它不是你要找的吗?
  • 我想使用 services.GetRequiredService();在我的内部课程中。最好的方法是什么?如何在任何地方访问相同的服务实例?在静态类中保留一份副本是个好主意吗?
  • 这适用于所有其他类。如何将 DI 实例的实例存储在容器本身中。
  • 为什么需要它的实例?你想解决什么样的问题?
  • 在普通的控制台应用程序或工作服务中,我需要一个 IServiceProvider 的实例来解决依赖关系。

标签: c# asp.net-core .net-core


【解决方案1】:

老问题,但从未回答....

在处理已转换为较新版本 .net 的代码时,我发现这通常是必要的,并且我需要现有静态类中的服务。我过去所做的是将 builder 创建的主机公开为程序类的公共静态属性。对于某些项目,IHost 不起作用。例如,最近的一个 AWS Lambda 项目有一个本地入口点和一个 lambda 入口点,一个是 IHost,一个是 IWebHost,这两个不共享一个公共接口。因此,我在 Startup 类中添加了一个静态“IServiceProvider Services”属性,并在 Configure 例程中将其设置为 app.ApplicationServices,以便其他类可以访问它。这被微软特别推荐为“避免静态访问服务。例如,避免将 IApplicationBuilder.ApplicationServices 捕获为静态字段或属性以在其他地方使用。” (https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines)

我使用的另一个选项是向您的类添加一个静态方法,该方法可以在主机启动后立即调用以将主机注入到类中。在 IHost.Build 之后,我添加了一个名为“InjectHost”之类的例程,它调用了我想要注入的每个类。

最近,我开始喜欢上了。我编写了以下启动代码和一个自定义属性做我自己的注入。从技术上讲,自定义属性甚至不是必需的。这是对优化的一种认可,但主要是为了明确说明某些类将被注入,这样新代码就没有机会与以前的开发人员可能已经投入的任何其他 DI 变通方法发生冲突。

public static class StaticDI {
    ///<summary>Add app.UseStaticDI to your "Configure" method to enable the dependency injection into static classes.</summary>
    public static IApplicationBuilder UseStaticDI(this IApplicationBuilder app) {
        var services = app.ApplicationServices;

        var assemblies = AppDomain.CurrentDomain.GetAssemblies();
        foreach (var assembly in assemblies) {
            //The attribute isn't technically required, I'm just hoping it speeds up startup.  If you don't want to
            //use it, just remove the .Where.
            var types = assembly.GetTypes().Where(x => x.IsDefined(typeof(StaticDIAttribute)));
            foreach(var type in types) {
                var fields = type.GetFields(BindingFlags.Static | BindingFlags.NonPublic);
                foreach (var field in fields) {
                    var diObject = services.GetService(field.FieldType);
                    if (diObject != null) {
                        field.SetValue(null, diObject);
                    }
                }
            }
        }

        return app;
    }
}
///<summary>Add this to static classes to enable dependency injection</summary>
public class StaticDIAttribute:Attribute {
    
}

只需从您的配置方法中调用“app.UseStaticDI”来进行注入。

但总的来说,请始终牢记 DI 的重点是不使用静态,它取代了您的静态。因此,“正确”的响应是基于接口将所有内容转换为实例类,最好使用新的静态类来容纳您的“AddBlahServices”扩展方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-09
    • 2012-01-13
    • 2016-09-01
    • 2021-06-07
    • 2021-09-07
    相关资源
    最近更新 更多