ASP. NET Core Source Code Reading Notes (2), asp. netcore

Source: Internet
Author: User

ASP. NET Core Source Code Reading Notes (2), asp. netcore

In the previous article, we mainly analyzed ASP. by default, NET Core depends on the storage and analysis of injection containers. This article mainly supplements some of the details ignored in the previous article: Service recycling, that is, service lifecycle. You can find the source code on GitHub.

This time, the main character is ServiceProvider. Almost all Source Code related to the lifecycle is concentrated in the ServiceProvider. cs file.

We know that the service life cycle consists of three types:

First, I come to the conclusion that there is no difference in the three life cycle categories. The life cycle of a service is determined by the container that provides the service, that is, the lifecycle of ServiceProvider. After a ServiceProvider is recycled, all services generated by it are also recycled. From this point of view, a ServiceProvider plays the role of a ServiceScoped. In fact, a servicprovider is essentially a ServiceProvider.

1 internal class ServiceScope: IServiceScope
 2     {
 3 // Only one read-only ServiceProvider field
 4 private readonly ServiceProvider _scopedProvider;
 5
 6 public ServiceScope (ServiceProvider scopedProvider)
 7 {
 8 _scopedProvider = scopedProvider;
 9         }
10
11 public IServiceProvider ServiceProvider
12 {
13 get {return _scopedProvider;}
14}
15
16 public void Dispose ()
17 {
18 _scopedProvider.Dispose ();
19}
20}
    So there is actually no ServiceScope, every scope is controlled by ServiceProvider. In this way, the Singleton service is the same as the Scoped service, because each program has an initial ServiceProvider, we can call it root, or its father, all other ServiceProviders are created by root (daddy), naturally Dad has the largest scope, so the Scoped service created by Dad is called Singleton because there is no ServiceProvider with a larger scope than root (Dad). If the roots are recovered, then the entire program should be over.

 1 public ServiceProvider (IEnumerable <ServiceDescriptor> serviceDescriptors)
 2         {
 3 _root = this;
 4 _table = new ServiceTable (serviceDescriptors);
 5
 6 _table.Add (typeof (IServiceProvider), new ServiceProviderService ());
 7 _table.Add (typeof (IServiceScopeFactory), new ServiceScopeService ());
 8 _table.Add (typeof (IEnumerable <>), new OpenIEnumerableService (_table));
 9         }
10
11 // This constructor is called exclusively to create a child scope from the parent
12 internal ServiceProvider (ServiceProvider parent)
13 {
14 // Note the following code
15 _root = parent._root;
16 _table = parent._table;
17}
    The two constructors of the ServiceProvider are posted above. Note the second constructor: the _root field refers to the root of the father, not the father. If ServiceProviderA (SPA) creates SPB, and SPB creates SPC, then the _root field of SPC refers to SPA. In other words, all ServiceProviders are not a layered structure, not a tree structure that we are familiar with, but a star structure. The first ServiceProvider of the application is in the middle, and the _root field of all other ServiceProviders are references In addition to the first ServiceProvider, all other ServiceProviders are equal. If SPC wants to create a Singleton type service, then just let _root (that is, SPA) create it.

    Since Singleton is Scoped, then we will focus on Scoped and Transient. The following is the source code of Scoped and Transient in ServiceProvider.

 1 internal class ServiceProvider: IServiceProvider, IDisposable
 2     {
 3 private readonly ServiceProvider _root;
 4 private readonly ServiceTable _table;
 5 private bool _disposeCalled;
 6
 7 // Scoped service mapping, used to release service instance
 8 private readonly Dictionary <IService, object> _resolvedServices = new Dictionary <IService, object> ();
 9 // Dispose list of one-time service
10 private List <IDisposable> _transientDisposables;
11
12 internal IServiceCallSite GetResolveCallSite (IService service, ISet <Type> callSiteChain)
13 {
14 IServiceCallSite serviceCallSite = service.CreateCallSite (this, callSiteChain);
15 if (service.Lifetime == ServiceLifetime.Transient)
16 {
17 return new TransientCallSite (serviceCallSite);
18}
19 else if (service.Lifetime == ServiceLifetime.Scoped)
20 {
21 return new ScopedCallSite (service, serviceCallSite);
twenty two             }
23 else
twenty four             {
25 return new SingletonCallSite (service, serviceCallSite);
26}
27}
28
29 private class TransientCallSite: IServiceCallSite
30 {
31 private readonly IServiceCallSite _service;
32 // Involve method is the key
33 public object Invoke (ServiceProvider provider)
34 {
35 // trigger and put into ServiceProvider's one-time service release list
36 return provider.CaptureDisposable (_service.Invoke (provider));
37}
38 // Omit the Build method
39}
40 private class ScopedCallSite: IServiceCallSite
41 {
42 private readonly IService _key;
43 private readonly IServiceCallSite _serviceCallSite;
44 // Invoke method is the key, omitting other unrelated methods
45 public virtual object Invoke (ServiceProvider provider)
46 {
47 object resolved;
48 // Put into ServiceProvider's Scoped service parsing list
49 lock (provider._resolvedServices)
50 {
51 // If the ResolvedService list is already cached, there is no need to create it
52 if (! Provider._resolvedServices.TryGetValue (_key, out resolved))
53 {
54 resolved = _serviceCallSite.Invoke (provider);
55 provider._resolvedServices.Add (_key, resolved);
56}
57}
58 return resolved;
59}
60}
    As can be seen from the ServiceProvider's GetResolvedCallSite method, when we want to parse a service, first generate different CallSite according to the service life cycle, and the Invoke method of different CallSite determines how the ServiceProvider manages these services.

    First look at TransientCallSite.Invoke (). It calls the private method of ServiceProvider: CaptureDisposable (). This method captures the service that implements the IDisposable interface. If the interface is implemented, it is placed in the _transientDisposables field of ServiceProvider. As the name implies, this field exists to release the service of the Transient type. If a service does not implement the IDisposable interface, then the ServiceProvider will not maintain a reference to it after the service ends. Since no variable has a reference to it, it will naturally be recycled by GC.

    Look at ScopedCallSite.Invoke () again. The first is to find the corresponding service in the _resolvedServices field of ServiceProvider, if it can be found, explain Created before, there is no need to create again. If it hasn't been created yet, put it in the _resolvedServices field cache in case it is needed. It seems that the Scoped type service does not have a special field management Dispose like the Transient service, because this is not required, the _resolvedServices field can be used as a cache and can also be used by Dispose.

    Take a look at the Dispose method of ServiceProvider:

        public void Dispose ()
        {
            lock (SyncObject)
            {
                if (_disposeCalled)
                {
                    return;
                }

                _disposeCalled = true;

                if (_transientDisposables! = null)
                {
                    foreach (var disposable in _transientDisposables)
                    {
                        disposable.Dispose ();
                    }

                    _transientDisposables.Clear ();
                }
                foreach (var entry in _resolvedServices)
                {
                    (entry.Value as IDisposable) ?. Dispose ();
                }

                _resolvedServices.Clear ();
            }
        }
    As can be seen from the above method, ServiceProvider treats _resolvedServices and _transientDisposables the same, and does not deliberately release Transient's services several times. The only difference between the Transient service and the Scoped service is that the Transient service will not go to the cache field to find out if there is already a cache before it is instantiated. If necessary, the ServiceProvider will instantiate one for you. All services created by ServiceProvider (either Transient or Scoped) will only be released when the ServiceProvider is released. This will bring a problem: if a ServiceProvider does not Dispose for a long time, then if it wants to resolve the Transient type service, it will occupy a lot of memory or even cause a memory leak. The more instances, the GC will also be affected. It can be imagined that the first ServiceProvider (that is, root) of ASP.NET Core will not resolve Transient services. It can create ServiceScope and resolve the service by their ServiceProvider.

    In order to avoid a large number of useless services remaining in memory, we have to release useless services. For example, in RenderView, the services related to Route must be useless. For this, different ServiceScopes can be created. Using the using statement can correctly release useless services.

1 void DoSthAboutRoute ()
2 {
3 using (IServiceScope serviceScope = serviceProvider.GetService <IServiceScopeFactory> (). CreateScope ())
4 {
5 IService routeService = serviceScope.ServiceProvider.GetService <IRouteService> ();
6 // ....
7}
8 }
    The using statement will automatically call the serviceScope.ServiceProvider.Dispose () method at the end, so all services created by the ServiceProvider will be released in time. At this time, the variable serviceScope has exceeded its scope and it will be marked as GC. Garbage object.

-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ----------------------

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.