[Asp.net 5] DependencyInjection project code analysis 4-Microsoft's implementation (5) (IEnumerable <> supplement ),
For the dependency injection series of Asp.net 5, refer to [Asp.net 5] DependencyInjection project code analysis-directory
When talking about Microsoft's implementation earlier, we did not talk about OpenIEnumerableService and ClosedIEnumerableService. Now we can complete this part.
We recall that the IEnumerable and new OpenIEnumerableService (_ table) relationships are registered in the ServiceProvider class Constructor (used externally.
Public ServiceProvider (IEnumerable <ServiceDescriptor> serviceDescriptors) {_ root = this; _ table = new ServiceTable (serviceDescriptors); _ table. add (typeof (IServiceProvider), new ServiceProviderService (); _ table. add (typeof (IServiceScopeFactory), new ServiceScopeService (); _ table. add (typeof (IEnumerable <>), new OpenIEnumerableService (_ table ));}ServiceProvider Constructor
Because IEnumerable is generic, we can infer that the OpenIEnumerableService class should implement the IGenericService interface. So what is the process between the implementation classes of IEnumerable <T>?
- [Step 1] First, the system will check whether the _ services (Dictionary <Type, ServiceEntry> Type) in ServiceTable has registered the IEnumerable <T> Type. If yes, the system will return the implementation class directly.Last. The following is why IEnumerable <T> is directly included:
- The system can display the registered IEnumerable <T>, such as services. AddTransient <IEnumerable <T>, List <T> ();
- The system may have previously obtained IEnumerable <T>, SO _ services has the cache of the previous result.
- [Step 2] if the corresponding IEnumerable <T> is not found, the system will continue to use _ genericServices (Dictionary <Type, List <IGenericService> Type) to find IEnumerable <>, different from obtaining through _ services, the IGenericService List (List type, including order) corresponding to the IEnumerable <> type obtained through _ genericServices is not a single IGenericService; then traverse the IGenericService list in sequenceIEnumerable <T>/IGenericService. GetService ()Add to services, and repeat the [Step 1] operation to obtain.
- If the system does not register the IEnumerable <> type, only the unique register item OpenIEnumerableService can be obtained in the _ genericServices list. The corresponding actions are performed in OpenIEnumerableService.
- If the system registers the IEnumerable <> type (for example, GenericService1), GenericService1 must be placed after OpenIEnumerableService in the registration list. Therefore, when obtaining IEnumerable <T>, OpenIEnumerableService. the returned values of GetService () and GenericService1.GetService () must be added to _ services, but GenericService1.GetService () must be later. Therefore, the Implementation class of IEnumerable <T> must be GenericService1.GetService (). createCallSite (). invoke () value;In other words, GenericService1 will overwrite OpenIEnumerableService.
- Because the registration of IEnumerable will overwrite the OpenIEnumerableService, in principle, you are not allowed to register IEnumerable <> type
OpenIEnumerableService and ClosedIEnumerableService
BecauseOpenIEnumerableServiceImplement the IGenericService interface. Therefore, an IService-type object is returned, which is of the ClosedIEnumerableService type. Within the ClosedIEnumerableService type, the registration items of all T of _ services in ServiceTable are actually returned, and then returned as IEnumerable <T> type.
Internal class OpenIEnumerableService: IGenericService {private readonly ServiceTable _ table; public OpenIEnumerableService (ServiceTable table) {_ table = table;} public ServiceLifetime Lifetime {get {return ServiceLifetime. transient ;}} public IService GetService (Type closedServiceType) {var itemType = closedServiceType. getTypeInfo (). genericTypeArguments [0]; ServiceEntry entry; return _ tabl E. TryGetEntry (itemType, out entry )? New ClosedIEnumerableService (itemType, entry): null ;}}OpenIEnumerableService internal class internal: IService {private readonly Type _ itemType; private readonly ServiceEntry _ serviceEntry; public ClosedIEnumerableService (Type itemType, ServiceEntry entry) {_ itemType = itemType; _ serviceEntry = entry ;} public IService Next {get; set;} public ServiceLifetime Lifetime {get {return ServiceLifetime. transient ;}} public IServiceCal LSite CreateCallSite (ServiceProvider provider, ISet <Type> callSiteChain) {var list = new List <IServiceCallSite> (); for (var service = _ serviceEntry. First; service! = Null; service = service. next) {list. add (provider. getResolveCallSite (service, callSiteChain);} return new CallSite (_ itemType, list. toArray ();} private class CallSite: IServiceCallSite {private readonly Type _ itemType; private readonly IServiceCallSite [] _ serviceCallSites; public CallSite (Type itemType, IServiceCallSite [] serviceCallSites) {_ itemType = itemType; _ serviceCallSites = serviceC AllSites;} public object Invoke (ServiceProvider provider) {var array = Array. CreateInstance (_ itemType, _ serviceCallSites. Length); for (var index = 0; index! = _ ServiceCallSites. length; ++ index) {array. setValue (_ serviceCallSites [index]. invoke (provider), index);} return array;} public Expression Build (Expression provider) {return Expression. newArrayInit (_ itemType, _ serviceCallSites. select (callSite => Expression. convert (callSite. build (provider), _ itemType )));}}}ClosedIEnumerableService
[Previously, when we introduced ServiceEntry, we clearly pointed out that it is a linked list structure, rather than storing a value separately. Its application has done its best here]