C# 一次又一次地构建服务提供商可以吗?

C# 一次又一次地构建服务提供商可以吗?,c#,.net-core,dependency-injection,C#,.net Core,Dependency Injection,在.NET核心控制台应用程序中,我希望使用内置的依赖项注入,包括自动处理链中的IDisposable对象。在ASP.NET内核中,对象是随着每个请求创建和处理的,这似乎是合理的 但在控制台应用程序中,我必须构建服务提供商,然后通过从提供商检索链中的第一项并运行以下命令来启动它: class Program { static void Main() { using var serviceProvider = new ServiceCollection()

在.NET核心控制台应用程序中,我希望使用内置的依赖项注入,包括自动处理链中的
IDisposable
对象。在ASP.NET内核中,对象是随着每个请求创建和处理的,这似乎是合理的

但在控制台应用程序中,我必须构建服务提供商,然后通过从提供商检索链中的第一项并运行以下命令来启动它:

class Program {
    static void Main() {
        using var serviceProvider = new ServiceCollection()
            .AddTransient<Program>()
            .AddTransient<Repository>()
            .AddTransient<MyContext>()
            .BuildServiceProvider();
        serviceProvider.GetService<Program>().Go();
    }

    private readonly Repository _repo;
    public Program(Repository repo) => _repo = repo;

    public void Go() => _repo.Go();
}

public class Repository : IDisposable {
    private readonly MyContext _context;
    public Repository(MyContext context) => _context = context;

    public void Go() => Console.WriteLine("Go!");
    public void Dispose() => Console.WriteLine("Repository Disposed!");
}

public class MyContext : DbContext {
    public override void Dispose() {
        base.Dispose();
        Console.WriteLine("MyContext Disposed!");
    }
}
如果我希望在循环中或使用计时器运行进程,则在服务提供程序本身被释放之前,不会释放任何内容:

for (int i = 0; i < 3; ++i)
{
    serviceProvider.GetService<Program>().Go();
}

Go!
Go!
Go!
Repository Disposed!
MyContext Disposed!
Repository Disposed!
MyContext Disposed!
Repository Disposed!
MyContext Disposed!
for(int i=0;i<3;++i)
{
serviceProvider.GetService().Go();
}
走!
走!
走!
处置库!
MyContext!
处置库!
MyContext!
处置库!
MyContext!
如果我每次都处置并重新创建服务提供商,我会得到想要的结果:

for (int i = 0; i < 3; ++i)
{
    using var serviceProvider = new ServiceCollection()
        .AddTransient<Program>()
        .AddTransient<Repository>()
        .AddTransient<MyContext>()
        .BuildServiceProvider();

    serviceProvider.GetService<Program>().Go();
}

Go!
Repository Disposed!
MyContext Disposed!
Go!
Repository Disposed!
MyContext Disposed!
Go!
Repository Disposed!
MyContext Disposed!
for(int i=0;i<3;++i)
{
使用var serviceProvider=newservicecollection()
.AddTransient()
.AddTransient()
.AddTransient()
.BuildServiceProvider();
serviceProvider.GetService().Go();
}
走!
处置库!
MyContext!
走!
处置库!
MyContext!
走!
处置库!
MyContext!
我的问题:像这样一次又一次地构建和处理提供者可以吗?我在考虑每隔几秒钟用定时器做一次。这是一个昂贵的过程吗?有没有更好的方法,不必在整个链中手动处理对象


或者这可能就是ASP.NET Core在幕后所做的,用每个请求重新创建提供程序吗?

简言之,不!我绝对不会每次都重新创建服务提供商。这似乎相当浪费

不,ASP.NETDI不是这样工作的,服务提供程序只在应用程序启动时构建

如果将
Program
更改为实现
IDisposable
,并将循环更改为

for(int i=0;i<3;++i)
{
使用(var program=serviceProvider.GetService()){
program.Go();
}
}
你应该得到同样的结果


未测试的

垃圾收集将在适当的时候处理注入对象,因此您不必担心。创建您可以使用的静态服务提供商:

static void Main() {
    serviceProvider = new ServiceCollection()
        .AddTransient<Program>()
        .AddTransient<Repository>()
        .AddTransient<MyContext>()
        .BuildServiceProvider();
    serviceProvider.GetService<Program>().Go();
}

private static IServiceProvider serviceProvider;
static void Main(){
serviceProvider=新的ServiceCollection()
.AddTransient()
.AddTransient()
.AddTransient()
.BuildServiceProvider();
serviceProvider.GetService().Go();
}
专用静态IServiceProvider服务提供商;

创建一个作用域,从中解析所有依赖项,然后处置该作用域。看起来是这样的:

using var serviceProvider = new ServiceCollection()
    .AddTransient<Program>()
    .AddTransient<Repository>()
    .AddTransient<MyContext>()
    .BuildServiceProvider();

for (int i = 0; i < 3; ++i)
{
    using var scope = serviceProvider.CreateScope();
    scope.ServiceProvider.GetService<Program>().Go();
}
使用var serviceProvider=new servicecolection()
.AddTransient()
.AddTransient()
.AddTransient()
.BuildServiceProvider();
对于(int i=0;i<3;++i)
{
使用var scope=serviceProvider.CreateScope();
scope.ServiceProvider.GetService().Go();
}

当服务作用域本身被释放时,服务作用域创建的所有作用域服务和临时服务都被释放。在本例中,所有涉及的服务都注册为transient,这意味着每次请求
程序时都会创建新实例。如果您要从同一服务范围多次请求
Program
,您将获得所有所需注册服务的多个实例。这些实例中的每一个都由服务范围跟踪,并与服务范围一起处理。

如果您更改
serviceProvider.GetService().Go()
您应该得到相同的结果-UNTESTEDProgram不是一次性的。另外,我只是试着让它成为一次性的,然后这样做,直到服务提供商被处理掉,它仍然不会处理孩子。它实际上是双重处理程序-一次是在
使用
完成时,另一次是在处理提供者时。这与我最初的示例没有什么不同-如果我继续使用相同的
服务提供者
而不处理它,那么其他对象都不会在我需要时被处理。我不想把它留给垃圾收集器-我想在处理完这些对象后正确地处理它们。@JoeEnos你可以调用。处理完你的一次性对象后再处理它?DI会为你做这件事,所以手动处理需要在每个项目上实现一次性,手动处理每个项目的子项,这是我试图避免的。但是,是的,如果我需要对处理过程进行超粒度控制,我可以做到。好主意。当您使用(var scope=serviceProvider.CreateScope()){scope.serviceProvider.GetService().Go();}
将代码更改为
时,它也可以在没有循环的情况下工作。
using var serviceProvider = new ServiceCollection()
    .AddTransient<Program>()
    .AddTransient<Repository>()
    .AddTransient<MyContext>()
    .BuildServiceProvider();

for (int i = 0; i < 3; ++i)
{
    using var scope = serviceProvider.CreateScope();
    scope.ServiceProvider.GetService<Program>().Go();
}