[Asp.net 5] DependencyInjection project code analysis 4-Microsoft's implementation (I ),
In the previous two implementations, many internal details are unknown. Microsoft's framework is also designed to block specific implementations, so we can only focus on interfaces. However, people are curious. How is dependency injection implemented?
How does Microsoft implement it? Next we will present them one by one (to be honest, the code is really hard to read)
Let's take a look at the core classes:ServiceTable
Internal class ServiceTable {private readonly object _ sync = new object (); private readonly Dictionary <Type, ServiceEntry> _ services; private readonly Dictionary <Type, List <IGenericService> _ genericServices; private readonly ConcurrentDictionary <Type, Func <ServiceProvider, object> _ realizedServices = new ConcurrentDictionary <Type, Func <ServiceProvider, object> (); public ServiceTable (IEnumerable <ServiceDescriptor> descriptors) {_ services = new Dictionary <Type, ServiceEntry> (); _ genericServices = new Dictionary <Type, List <IGenericService> (); foreach (var descriptor in descriptors) {var serviceTypeInfo = descriptor. serviceType. getTypeInfo (); if (serviceTypeInfo. isGenericTypeDefinition) {Add (descriptor. serviceType, new GenericService (descriptor);} else if (descriptor. implementationIns Tance! = Null) {Add (descriptor. ServiceType, new InstanceService (descriptor);} else if (descriptor. ImplementationFactory! = Null) {Add (descriptor. serviceType, new FactoryService (descriptor);} else {Add (descriptor. serviceType, new Service (descriptor) ;}} public ConcurrentDictionary <Type, Func <ServiceProvider, object >>realizedservices {get {return _ RealizedServices ;}} public bool TryGetEntry (Type serviceType, out ServiceEntry entry) {lock (_ sync) {if (_ services. tryGetValue (serviceType, out entry) {retur N true;} else if (serviceType. getTypeInfo (). isGenericType) {var openServiceType = serviceType. getGenericTypeDefinition (); List <IGenericService> genericEntry; if (_ genericServices. tryGetValue (openServiceType, out genericEntry) {foreach (var genericService in genericEntry) {var closedService = genericService. getService (serviceType); if (closedService! = Null) {Add (serviceType, closedService) ;}} return _ services. tryGetValue (serviceType, out entry) ;}} return false;} public void Add (Type serviceType, IService service) {lock (_ sync) {ServiceEntry entry; if (_ services. tryGetValue (serviceType, out entry) {entry. add (service) ;}else {_ services [serviceType] = new ServiceEntry (service) ;}} public void Add (Type serviceType, IGenericService GenericService) {lock (_ sync) {List <IGenericService> genericEntry; if (! _ GenericServices. tryGetValue (serviceType, out genericEntry) {genericEntry = new List <IGenericService> (); _ genericServices [serviceType] = genericEntry;} genericEntry. add (genericService );}}}View Code
First, check the Code attributes:
private readonly Dictionary<Type, ServiceEntry> _services;private readonly Dictionary<Type, List<IGenericService>> _genericServices;private readonly ConcurrentDictionary<Type, Func<ServiceProvider, object>> _realizedServices = new ConcurrentDictionary<Type, Func<ServiceProvider, object>>();
The ServiceEntry class is described later. It can be used as a class that can generate an object. Therefore, "_ services" is a dictionary that stores the relationship between types and instances.
"_ GenericServices", as its name implies, must be related to generics. In fact, "_ genericServices" Type is similar to "List <T>" Type that contains generic definitions.
The last _ realizedServices definition is complicated. You may not understand what it means. Actually, a Dictionary table (ConcurrentDictionary) is defined. Different from a Dictionary table, ConcurrentDictionary provides better support for multithreading. The first generic real parameter of the dictionary table is Type, and the second parameter is a Func proxy. The input parameter of this Func proxy (which can be used in Baidu C # + Func query) is ServiceProvider, the returned value is object.
[When an instance is obtained through injection, the system queries whether _ realizedServices already contains the instance. If yes, the system queries the instance. If no, it generates one in _ services and then adds the generated result to realizedServices, and return; but if _ services does not exist, the generic type in _ genericServices will be "real parameter", and all types of matching will be added to _ services, and get it from _ services.]
From the class constructor, we can find that the order of constructing IService (IGenericService) based on ServiceDescriptor object is: GenericService-> InstanceService-> FactoryService-> Service (different from Autofac and Ninject ).
Interface Definition:IGenericService, IService, IServiceCallSite
internal interface IGenericService { ServiceLifetime Lifetime { get; } IService GetService(Type closedServiceType); }
internal interface IService { IService Next { get; set; } ServiceLifetime Lifetime { get; } IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain); }
internal interface IServiceCallSite { object Invoke(ServiceProvider provider); Expression Build(Expression provider); }
IServiceCallSite interface: similar to a factory class, the interface instance is responsible for generating object objects through Invoke calls, that is, objects generated after dependency injection.
IService interface: Life Cycle. CreateCallSite creates IServiceCallSite through ServiceProvider, that is, it can indirectly create instance objects. I think it is not appropriate to put the Next attribute in the interface. The Next object references itself, so that IService has a chain structure.
IGenericService interface: generates an IService interface.
[There is a question that can be considered here. IGenericService and IService are easy to understand by designing two interfaces. There is a large gap between generics and non-generics, so you can not share one interface. But why cannot I combine the IService interface and IServiceCallSite interface into an interface? The interface definition is as follows:
internal interface IService { ServiceLifetime Lifetime { get; } object Invoke(ServiceProvider provider); }
]
ServiceEntry class
Internal class ServiceEntry {private object _ sync = new object (); public ServiceEntry (IService service) {First = service; Last = service;} public IService First {get; private set ;} public IService Last {get; private set;} public void Add (IService service) {lock (_ sync) {Last. next = service; Last = service ;}}}ServiceEntry
The ServiceEntry class is relatively simple. It is equivalent to an IService one-way linked list, which then contains the Start Node and End Node of the linked list. The Add method adds a new node to the end of the linked list.
[I think it is not a good design to avoid using the list structure like LinkList or writing a list of generic classes by myself and adding self-reference in IService.]
Let's look back.ServiceTableOfDictionary <Type, ServiceEntry> _ servicesAttribute, in fact, many IService interfaces are registered for a certain type, and these interfaces are stored in the list order.
Look backServiceTable
The two Add methods are very simple. You can determine whether a list or array of the Type exists. If yes, call the Add method of the list or array. If not, create one.
The interesting thing is that the TryGet method searches for non-generic objects first. If not, searches for generic objects. And "real parameters" of the generic type ". This code is worth further research.
Public bool TryGetEntry (Type serviceType, out ServiceEntry entry) {lock (_ sync) {if (_ services. tryGetValue (serviceType, out entry) {return true;} else if (serviceType. getTypeInfo (). isGenericType) {var openServiceType = serviceType. getGenericTypeDefinition (); List <IGenericService> genericEntry; if (_ genericServices. tryGetValue (openServiceType, out genericEntry) {foreach (var genericService I N genericEntry) {var closedService = genericService. GetService (serviceType); if (closedService! = Null) {Add (serviceType, closedService) ;}return _ services. TryGetValue (serviceType, out entry) ;}}} return false ;}TryGetEntry
[ServiceTableDictionary <Type,ServiceEntry> _ Services and Dictionary <Type,List <IGenericService>> _ GenericServices one of the two attributes uses a one-way list and one uses a list. Friends with cleanliness may feel uncomfortable, whether or not. Why can't we unify them to make us comfortable?]