在配置方法中使用服务时超出范围? [英] outside of scope when use a service in Configure method?

查看:20
本文介绍了在配置方法中使用服务时超出范围?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下是我教科书中的伪代码,我对在配置方法中使用服务感到困惑

Below is pseudo code from my textbook, and I'm confused about use a service in configure method

public class ConcreteA
{
   public static Run(IServiceProvider serviceProvider)
   {
      ConcreteB _concrete = serviceProvider.GetRequiredService<ConcreteB>();
      ... //use ConcreteB  instance
   }

}
__________________________________________________________

// startup.cs
public void ConfigureServices(IServiceCollection services) 
{
   services.AddScoped<ConcreteA>;
   services.AddScoped<ConcreteB>;  
}

public void Configure(IApplicationBuilder app) {
   app.UseStatusCodePages();
   app.UseDeveloperExceptionPage();
   app.UseMvcWithDefaultRoute();
   ConcreteA.Run(app.ApplicationServices);
}

有人告诉我,因为我在 Configure 中使用了 ConcreteA 方法,所以我在范围之外运行它.我创建的任何依赖项(在本例中为 ConcreteB 实例)都会存在.

I was told that becuase I use ConcreteA in Configure ,method, so I'm running this outside of a scope. Any dependency(ConcreteB instance in this case) I create will hang around.

我很困惑,以下是我的问题:

I'm very confused, below is my questions:

Q1- 我将 ConcreteAConcreteB 都注册为 AddScoped,所以不应该有任何捕获的依赖问题,因为它们是相同的范围,那么为什么 ConcreteB 仍然会出现.

Q1- I registered ConcreteA and ConcreteB both as AddScoped, so there shouldn't be any captured dependencies issues as they are in same scope, so why ConcreteB will still hang around.

Q2- 我什至没有创建 ConcreteA 实例,因为我访问的方法是静态方法,所以没有需要创建 ConcreteA 实例.所以ConcreteB就更不可能到处闲逛了.

Q2- I didn't even create a ConcreteA instance as the method I access is a static method, so no ConcreteA instance needs to be created. So it is even more impossible for ConcreteB to hang around.

推荐答案

明确回答您的困惑:您必须将服务提供者视为对象的缓存.当它创建一个未注册为瞬态的服务时,它将在本地存储该实例,以便稍后再次提供相同实例.

To answer in regards to your confusion explicitly: You have to think about the service provider as a cache of objects. When it creates a service that is not registered as transient, then it will store that instance locally so that it can provide the same instance again at a later time.

例如,当您执行以下操作时(假设 IFoo 未注册为瞬态),那么它将解析相同的对象实例:

For example, when you do the following (assuming IFoo is not registered as transient), then it will resolve the same object instance:

serviceProvider.GetService<IFoo>();
serviceProvider.GetService<IFoo>();

为了做到这一点,服务提供者必须记住它在第一次调用中返回的 IFoo,因此它可以在第二次调用(和任何其他调用)中返回相同的实例.

In order to do that, the service provider has to remember the IFoo it returned in the very first call, so it can return the same instance on the second call (and any other call).

那么什么是范围?范围基本上告诉服务提供者从单独缓存解析服务.当您在一个作用域内并且您现在解析一个作用域服务 IScopedBar,那么服务提供者为您创建的实例将被缓存在该作用域缓存中:

So what is a scope? A scope basically tells the service provider to resolve services from a separate cache. When you are within a scope and you now resolve a scoped service IScopedBar, then the instance the service provider creates for you will be cached in that scoped cache:

serviceProvider.GetService<IScopedBar>();
serviceProvider.GetService<IScopedBar>();

当您在一个范围内并解析一个单例服务时,该服务仍将在主缓存中查找.但是会在范围缓存中查找范围服务.

When you are within a scope and you resolve a singleton service, then that service will still be looked up in the main cache. But scoped services will be looked up in the scoped cache.

如果不关闭作用域,那一切都不会真正产生影响.当作用域关闭时,它在被释放时完成(例如,当 using 块结束时),然后作用域缓存中的服务被释放并清除缓存.但是主缓存会保留.

That all wouldn’t really make a difference if a scope wouldn’t be closed. When a scope is closed, which is done when it gets disposed (e.g. when the using block ends), then the services in the scoped cache are disposed and the cache is cleared. The main cache however stays.

如果我们在一个简化的伪服务提供者类中实现它,它可能看起来像这样:

If we were to implement that in a simplified pseudo service provider class, it could look like this:

public class SimplifiedServiceProvider
{
    private Dictionary<Type, object> mainCache = new Dictionary<Type, object>();
    private Dictionary<Type, object> scopeCache = new Dictionary<Type, object>();

    public object GetService(Type type)
    {
         var serviceLifetime = GetLifetimeForService(type);

         if (serviceLifetime == ServiceLifetime.Transient)
         {
             // transients are created directly
             return CreateNewInstance(type);
         }
         else if (serviceLifetime == ServiceLifetime.Singleton)
         {
             // try to get from the cache
             if (!mainCache.TryGetValue(type, out var service))
             {
                 // create the service first
                 service = CreateNewInstance(type);
                 mainCache.Add(type, service);
             }
             return service;
         }
         else if (serviceLifetime == ServiceLifetime.Scoped)
         {
             // try to get from the scope cache
             if (!scopeCache.TryGetValue(type, out var service))
             {
                 // create the service first
                 service = CreateNewInstance(type);
                 scopeCache.Add(type, service);
             }
             return service;
         }
    }

    public void DisposeScope()
    {
        // dispose all created (disposable) instances
        foreach (var instance in scopeCache.Values)
            (instance as IDisposable)?.Dispose();

        // reset cache
        scopeCache.Clear();
    }

    private ServiceLifetime GetLifetimeForService(Type type) { … }
    private object CreateNewInstance(Type type) { … }
}

(服务提供者和服务范围的实际实现显然比这要复杂一些,但这仍然可以很好地说明范围依赖与单例之间的区别.)

(The real implementation of a service provider and service scopes is obviously a bit more complicated than this, but this should still give a good idea of how scoped dependencies differ from singletons.)

考虑到这个想法和伪实现,想象一下当你解析一个范围外的范围服务时会发生什么,所以 DisposeScope 永远不会被调用:创建的范围服务将永远留在范围缓存;就像单身人士永远留在主缓存中一样.

With that idea and pseudo implementation in mind, imagine what happens when you resolve a scoped service outside of a scope, so DisposeScope would never be called: The created scoped service would just stay permanently inside of the scope cache; just like singletons stay permanently within the main cache.

因此,通过解析服务范围之外的范围服务,您可以有效地将该实例的生命周期提升为单例服务.这不会影响实际上在一个范围内创建的实例,但那些在范围外创建的实例将在服务提供者的生命周期内存活,这通常是应用程序的生命周期.

So by resolving a scoped service outside of a service scope, you effectively lifted the lifetime of that instance up to be a singleton service. This will not affect instances that actually are created within a scope, but those instances that are created outside of a scope will live for the lifetime of the service provider, which usually is the lifetime of the application.

这就是为什么当您想要使用自然作用域"之外的作用域服务(即自动为您创建的作用域,就像 ASP.NET Core 在处理请求时所做的那样)时,您通常希望创建一个临时作用域.这样,您就限制了该作用域的生命周期,因此也限制了您解析的实例的生命周期.

That is why you usually want to create a temporary scope when you want to consume scoped services outside of "natural scopes" (i.e. scopes that are automatically created for you, like ASP.NET Core does when handling a request). That way, you restrict the lifetime of that scope, and as such also the lifetime of the instances that you resolve.

这篇关于在配置方法中使用服务时超出范围?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆