標籤:
Asp.net 5的依賴注入注入系列可以參考連結: [Asp.net 5] DependencyInjection項目程式碼分析-目錄
我們在之前講微軟的實現時,對於OpenIEnumerableService與ClosedIEnumerableService拋下沒講,現在我們就將該部分補充完整。
我們回憶ServiceProvider類的建構函式(對外部使用的)中,註冊了IEnumerable<>、new OpenIEnumerableService(_table)的關係。
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建構函式
因為IEnumerable是泛型,所以我們可以推斷OpenIEnumerableService類應該實現IGenericService介面。那麼如果我們想尋找IEnumerable<T>的實作類別中間會有怎樣的過程呢?
- [步驟1]首先系統會尋找在ServiceTable內部_services(Dictionary<Type, ServiceEntry>類型)是否有註冊過IEnumerable<T>類型,如果有註冊直接返回實作類別中最後一個。下面是直接包含IEnumerable<T>的原因
- 系統可以顯示的註冊IEnumerable<T>,比如services.AddTransient<IEnumerable<T>, List<T>>();
- 系統也可能之前擷取過IEnumerable<T>,所以_services中有上次結果的緩衝。
- [步驟2]如果沒有找到相應的IEnumerable<T>,系統會繼續通過_genericServices(Dictionary<Type, List<IGenericService>>類型)尋找IEnumerable<>,與通過_services方式擷取不同,通過_genericServices會擷取到的是所有註冊過IEnumerable<>類型對應的IGenericService列表(List類型,包含順序)並不是單一一個IGenericService;之後順序遍曆IGenericService列表,將IEnumerable<T>/IGenericService.GetService()的對應關係添加到services中,重複[步驟1]的操作擷取。
- 如果系統沒額外註冊IEnumerable<>類型,那麼_genericServices的列表中只能擷取唯一的註冊項OpenIEnumerableService,那麼相應的操作則在OpenIEnumerableService中進行。
- 如果系統額外註冊IEnumerable<>類型(假設為GenericService1),那麼在註冊列表中GenericService1一定排在OpenIEnumerableService之後。所以當擷取IEnumerable<T>時,OpenIEnumerableService.GetService()與GenericService1.GetService()傳回值一定都會添加到_services中,但是GenericService1.GetService()一定在後面,所以IEnumerable<T>的實作類別一定是GenericService1.GetService().CreateCallSite().Invoke()的值;換句話說GenericService1會將OpenIEnumerableService覆蓋掉。
- 由於註冊IEnumerable<>會覆蓋掉OpenIEnumerableService,所以原則上不允許註冊IEnumerable<>類型
OpenIEnumerableService與ClosedIEnumerableService
由於OpenIEnumerableService實現IGenericService介面,所以會返回IService類型的對象,該對象是ClosedIEnumerableService類型。ClosedIEnumerableService類型內部實際上返回的是ServiceTable中_services所有T的註冊項,之後以IEnumerable<T>類型返回。
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 _table.TryGetEntry(itemType, out entry) ? new ClosedIEnumerableService(itemType, entry) : null; } }
OpenIEnumerableService
internal class ClosedIEnumerableService : 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 IServiceCallSite 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 = serviceCallSites; } 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
[之前我們介紹ServiceEntry時,明確指出是鏈表結構,而不是單獨存放一個值;其應用在這進行了淋漓盡致的表現]
[Asp.net 5] DependencyInjection項目程式碼分析4-微軟的實現(5)(IEnumerable<>補充)