經過一段時間的準備,新的一期【ASP.NET Core MVC開發實戰之商城系統】已經開始,在之前的文章中,講解了商城系統的整體功能設計,頁面佈局設計,環境搭建,系統配置,及首頁商品類型,banner條,友情鏈接等功能的開發,今天繼續講解首頁的降價促銷,新品爆款等內容,僅供學習分享使用,如有不足之處... ...
上一節聊了一下 CallSite
是怎樣生成的,這一節我們來看一下 CallSite
是如何使用的。
入口
先讓我們來回顧一下 CreateServiceAccessor
這個方法。
private Func<ServiceProviderEngineScope, object?> CreateServiceAccessor(Type serviceType)
{
//通過 服務類型 獲取 callSite
ServiceCallSite? callSite = CallSiteFactory.GetCallSite(serviceType, new CallSiteChain());
if (callSite != null)
{
if (callSite.Cache.Location == CallSiteResultCacheLocation.Root)
{
//直接解析
object? value = CallSiteRuntimeResolver.Instance.Resolve(callSite, Root);
}
return _engine.RealizeService(callSite);
}
}
這段代碼跟 CallSite
有關的一共有三個地方,分別是 GetCallSite
和 Resolve(callSite,Root)
以及 _engine.RealizeService
。其中 GetCallSite
是用來生成 CallSite
的(也就是上一節的主要內容),而剩下的兩個則是對於 CallSite
的使用,也是這一節的主要內容。
RealizeService
我們先看一下 _engine.RealizeService
方法。
public override Func<ServiceProviderEngineScope, object?> RealizeService(ServiceCallSite callSite)
{
return scope =>
{
var result = CallSiteRuntimeResolver.Instance.Resolve(callSite, scope);
return result;
};
}
我們可以發現最終調用的還是 CallSiteRuntimeResolver.Instance.Resolve
這個方法,所以其實歸根結底對 CallSite
的調用其實最終就是一個地方,也就是這個 Resolve
方法。
CallSiteRuntimeResolver.Resolve
public object? Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
{
// 如果在 root scope 範圍里已經有緩存了,直接返回
if (scope.IsRootScope && callSite.Value is object cached)
{
return cached;
}
//調用 VisitCallSite 進行解析
return VisitCallSite(callSite, new RuntimeResolverContext
{
Scope = scope
});
}
VisitCallSite
protected virtual TResult VisitCallSite(ServiceCallSite callSite, TArgument argument)
{
switch (callSite.Cache.Location)
{
case CallSiteResultCacheLocation.Root:
return VisitRootCache(callSite, argument);
case CallSiteResultCacheLocation.Scope:
return VisitScopeCache(callSite, argument);
case CallSiteResultCacheLocation.Dispose:
return VisitDisposeCache(callSite, argument);
case CallSiteResultCacheLocation.None:
return VisitNoCache(callSite, argument);
default:
throw new ArgumentOutOfRangeException();
}
}
VisitCallSite
會根據 Location 進行分支處理,Location 是 CallSite 里的一個屬性。其中 Root 對應 Singleton,Scope 對應 Scope 生命周期,Dispose 對應 Trasient,None可以先將其理解為Singleton。
VisitRootCache(Single)
protected override object? VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
{
// Singleton 懶載入 如果有 value 直接返回
if (callSite.Value is object value)
{
// Value already calculated, return it directly
return value;
}
var lockType = RuntimeResolverLock.Root;
//在 root 範圍進行解析
ServiceProviderEngineScope serviceProviderEngine = context.Scope.RootProvider.Root;
// 鎖住 callSite 防止重覆生成value
lock (callSite)
{
// Lock the callsite and check if another thread already cached the value
// 可能其他地方已經生成了,在獲取一下看看
if (callSite.Value is object callSiteValue)
{
return callSiteValue;
}
//最終依舊是調用了 VisitCallSiteMain 方法
object? resolved = VisitCallSiteMain(callSite, new RuntimeResolverContext
{
Scope = serviceProviderEngine,
AcquiredLocks = context.AcquiredLocks | lockType
});
serviceProviderEngine.CaptureDisposable(resolved);
//進行緩存
callSite.Value = resolved;
return resolved;
}
}
VisitScopeCache(Scope)
// Check if we are in the situation where scoped service was promoted to singleton
// and we need to lock the root
// Scope 依賴 Singleton
return context.Scope.IsRootScope ?
VisitRootCache(callSite, context) :
VisitCache(callSite, context, context.Scope, RuntimeResolverLock.Scope);
private object? VisitCache(
ServiceCallSite callSite,
RuntimeResolverContext context,
ServiceProviderEngineScope serviceProviderEngine,
RuntimeResolverLock lockType)
{
bool lockTaken = false;
object sync = serviceProviderEngine.Sync;
// Dictionary<ServiceCacheKey, object?> ResolvedServices { get; } 緩存
Dictionary<ServiceCacheKey, object?> resolvedServices = serviceProviderEngine.ResolvedServices;
// Taking locks only once allows us to fork resolution process
// on another thread without causing the deadlock because we
// always know that we are going to wait the other thread to finish before
// releasing the lock
// 鎖住 sync 對象
if ((context.AcquiredLocks & lockType) == 0)
{
Monitor.Enter(sync, ref lockTaken);
}
try
{
//獲取鎖之後
// Note: This method has already taken lock by the caller for resolution and access synchronization.
// For scoped: takes a dictionary as both a resolution lock and a dictionary access lock.
//先訪問緩存
if (resolvedServices.TryGetValue(callSite.Cache.Key, out object? resolved))
{
return resolved;
}
//解析
resolved = VisitCallSiteMain(callSite, new RuntimeResolverContext
{
Scope = serviceProviderEngine,
AcquiredLocks = context.AcquiredLocks | lockType
});
//緩存需要釋放的實例
serviceProviderEngine.CaptureDisposable(resolved);
//放入緩存
resolvedServices.Add(callSite.Cache.Key, resolved);
return resolved;
}
finally
{
//解鎖
if (lockTaken)
{
Monitor.Exit(sync);
}
}
}
VisitDisposeCache(Transient)
protected override object? VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
{
//解析
var instance=VisitCallSiteMain(transientCallSite, context);
return context.Scope.CaptureDisposable(instance);
}
VisitNoCache(None)
protected virtual TResult VisitNoCache(ServiceCallSite callSite, TArgument argument)
{
return VisitCallSiteMain(callSite, argument);
}
VisitCallSiteMain
觀察以上方法,我們可以發現無論是 VisitRootCache還是VisitScopeCache 等等,最終都是調用 VisitCallSiteMain
這個方法來生成的實例。
protected virtual TResult VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
{
//callSite.kind 是只讀屬性 ,在 GetCallSite 時確定,根據 CallSite 類型確定(例 ConstantCallSite)
switch (callSite.Kind)
{
case CallSiteKind.Factory:
return VisitFactory((FactoryCallSite)callSite, argument);
case CallSiteKind.IEnumerable:
return VisitIEnumerable((IEnumerableCallSite)callSite, argument);
case CallSiteKind.Constructor:
return VisitConstructor((ConstructorCallSite)callSite, argument);
case CallSiteKind.Constant:
return VisitConstant((ConstantCallSite)callSite, argument);
case CallSiteKind.ServiceProvider:
return VisitServiceProvider((ServiceProviderCallSite)callSite, argument);
default:
throw new NotSupportedException(SR.Format(SR.CallSiteTypeNotSupported, callSite.GetType()));
}
}
VisitConstructor
其中比較複雜的就是這個 VisitConstructor
方法,通過反射來構造實例,主要思路是拿到實例類型的構造函數,然後通過遞歸(調用VisitCallSite
(見本文最上方))準備構造函數所需要的參數,最後調用 invoke 來生成實例。
protected override object VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
{
object?[] parameterValues;
//獲取構造需要使用的參數
//無參
if (constructorCallSite.ParameterCallSites.Length == 0)
{
parameterValues = Array.Empty<object>();
}
//有參
else
{
parameterValues = new object?[constructorCallSite.ParameterCallSites.Length];
for (int index = 0; index < parameterValues.Length; index++)
{
//遞歸解析 VisitCallSite 見上文
parameterValues[index] = VisitCallSite(constructorCallSite.ParameterCallSites[index], context);
}
}
#if NETFRAMEWORK || NETSTANDARD2_0
try
{
return constructorCallSite.ConstructorInfo.Invoke(parameterValues);
}
catch (Exception ex) when (ex.InnerException != null)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
// The above line will always throw, but the compiler requires we throw explicitly.
throw;
}
#else
return constructorCallSite.ConstructorInfo.Invoke(BindingFlags.DoNotWrapExceptions, binder: null,
parameters: parameterValues, culture: null);
#endif
}
VisitFactory
protected override object VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
{
//調用 Factory(Func<IServiceProvider, object> Factory) 委托(委托由開發者實現)
return factoryCallSite.Factory(context.Scope);
}
VisitIEnumerable
protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context)
{
//創建枚舉類型的數組
var array = Array.CreateInstance(
enumerableCallSite.ItemType,
enumerableCallSite.ServiceCallSites.Length);
//給數組填充值
for (int index = 0; index < enumerableCallSite.ServiceCallSites.Length; index++)
{
object? value = VisitCallSite(enumerableCallSite.ServiceCallSites[index], context);
array.SetValue(value, index);
}
return array;
}
VisitConstant
protected override object? VisitConstant(ConstantCallSite constantCallSite, RuntimeResolverContext context)
{
//直接返回保存的實例
return constantCallSite.DefaultValue;
}
VisitServiceProvider
protected override object VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, RuntimeResolverContext context)
{
return context.Scope;
}
總結
先根據實例的生命周期進行分支判斷,接下來根據服務的生成方式進行分支判斷。