public void ShouldCallImplementedInterfaceMethod() { var random = new Random(); for (int i = 0; i < 100; i++) { var interceptor1 = new Interceptor1(); var interceptor2 = new Interceptor2(); var interceptor3 = new Interceptor3(); var interceptor4 = new Interceptor4(); var interceptors = new IResponseCachingInterceptor[] { interceptor1, interceptor2, interceptor3, interceptor4 }; //随机组合进行测试 interceptors = interceptors.OrderBy(_ => random.Next()).ToArray(); Console.WriteLine($"time: {i:D3} - order:{string.Join(" -> ", interceptors.Select(m => m.GetType().Name))}"); var interceptorAggregator = new InterceptorAggregator(interceptors); interceptorAggregator.OnCacheStoringAsync(null, null, null, (_, _, _) => Task.FromResult <ResponseCacheEntry>(null)); interceptorAggregator.OnResponseWritingAsync(null, null, (_, _) => Task.FromResult(true)); NormalCheck(interceptors); } }
// Local function that creates and assigns the compiled setter static void SetInterceptorsSetter(InterceptorAggregator <TInterceptor> theAggregator) { lock (Lock) { if (InterceptorsSetter != null) { return; } try { var aggregatorBaseType = theAggregator.GetType().BaseType !; // Null out the field, which AggregateInterceptors() does not handle for us (because it does not expect reinitialization) var interceptorField = aggregatorBaseType.GetField("_interceptor", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) ?? throw new Exception($"Field {aggregatorBaseType.Name}._interceptor does not exist."); var resolvedField = aggregatorBaseType.GetField("_resolved", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) ?? throw new Exception($"Field {aggregatorBaseType.Name}._resolved does not exist."); // Compile a fast expression tree // Omit the checks, trusting that things will be the same in the future { var aggregateInterceptorsMethod = typeof(IInterceptorAggregator).GetMethod(nameof(IInterceptorAggregator.AggregateInterceptors)) ?? throw new Exception($"Type {nameof(IInterceptorAggregator)} does not have a single public method named {nameof(IInterceptorAggregator.AggregateInterceptors)}."); // InterceptorAggregator<TInterceptor> aggregator; var aggregatorParam = Expression.Parameter(typeof(InterceptorAggregator <TInterceptor>), "aggregator"); // TInterceptor[] interceptors; var interceptorsParam = Expression.Parameter(typeof(TInterceptor[]), "interceptors"); // SomeCustomAggregator theAggregator; var convertedAggregatorVariable = Expression.Variable(aggregatorBaseType, "theAggregator"); // theAggregator = (SomeCustomAggregator)aggregator; var aggregatorVariableAssignment = Expression.Assign(convertedAggregatorVariable, Expression.Convert(aggregatorParam, aggregatorBaseType)); // theAggregator._interceptor = null; var interceptorFieldExpression = Expression.Field(convertedAggregatorVariable, interceptorField); var setInterceptors = Expression.Assign(interceptorFieldExpression, Expression.Constant(null, interceptorField.FieldType)); // theAggregator._resolved = false; var resolvedFieldExpression = Expression.Field(convertedAggregatorVariable, resolvedField); var setResolved = Expression.Assign(resolvedFieldExpression, Expression.Constant(false)); // theAggregator.AggregateInterceptors(interceptors); var callAggregateInterceptors = Expression.Call(convertedAggregatorVariable, aggregateInterceptorsMethod, interceptorsParam); // Block(theAggregator): // theAggregator = (SomeCustomAggregator)aggregator; // theAggregator._interceptor = null; // theAggregator._resolved = false; // theAggregator.AggregateInterceptors(interceptors); var blockExpression = Expression.Block(variables: new[] { convertedAggregatorVariable }, aggregatorVariableAssignment, setInterceptors, setResolved, callAggregateInterceptors); var lambda = Expression.Lambda <Action <InterceptorAggregator <TInterceptor>, TInterceptor[]> >(blockExpression, aggregatorParam, interceptorsParam); InterceptorsSetter = lambda.Compile(); } } catch (Exception e) { throw ThrowHelper.ThrowIncompatibleWithEfVersion(e); } } }
public static void SetInterceptors(InterceptorAggregator <TInterceptor> aggregator, TInterceptor[] interceptors) { System.Diagnostics.Debug.Assert(aggregator != null); if (InterceptorsSetter is null) { SetInterceptorsSetter(aggregator); } System.Diagnostics.Debug.Assert(InterceptorsSetter != null); InterceptorsSetter(aggregator, interceptors);
public void ShouldShortCircuits() { var random = new Random(); for (int i = 0; i < 100; i++) { var interceptor1 = new Interceptor1(); var interceptor2 = new Interceptor2(); var interceptor3 = new Interceptor3(); var interceptor4 = new Interceptor4(); var shortCircuitsInterceptor = new ShortCircuitsInterceptor(); var interceptors = new IResponseCachingInterceptor[] { interceptor1, interceptor2, interceptor3, interceptor4, shortCircuitsInterceptor }; //随机组合进行测试 interceptors = interceptors.OrderBy(_ => random.Next()).ToArray(); Console.WriteLine($"time: {i:D3} - order:{string.Join(" -> ", interceptors.Select(m => m.GetType().Name))}"); var interceptorAggregator = new InterceptorAggregator(interceptors); interceptorAggregator.OnCacheStoringAsync(null, null, null, (_, _, _) => Task.FromResult <ResponseCacheEntry>(null)); interceptorAggregator.OnResponseWritingAsync(null, null, (_, _) => Task.FromResult(true)); bool shortCircuited = false; foreach (var interceptor in interceptors) { if (shortCircuited) { ShouldNoAnyCalled(interceptor); } else { NormalCheck(interceptor); shortCircuited = interceptor is ShortCircuitsInterceptor; } } } }
/// <summary> /// <inheritdoc cref="ResponseCachingContext"/> /// </summary> /// <param name="metadatas">Action端点的 <see cref="EndpointMetadataCollection"/></param> /// <param name="cacheKeyGenerator"></param> /// <param name="responseCache"></param> /// <param name="cacheDeterminer"></param> /// <param name="options"></param> /// <param name="interceptorAggregator"></param> /// <param name="dumpStreamCapacity"></param> public ResponseCachingContext(EndpointMetadataCollection metadatas, ICacheKeyGenerator cacheKeyGenerator, IResponseCache responseCache, IResponseCacheDeterminer cacheDeterminer, ResponseCachingOptions options, InterceptorAggregator interceptorAggregator, int dumpStreamCapacity) { if (metadatas is null) { throw new ArgumentNullException(nameof(metadatas)); } MaxCacheableResponseLength = Checks.ThrowIfMaxCacheableResponseLengthTooSmall(Metadata <IMaxCacheableResponseLengthMetadata>()?.MaxCacheableResponseLength ?? options.MaxCacheableResponseLength); MaxCacheKeyLength = options.MaxCacheKeyLength; KeyGenerator = cacheKeyGenerator ?? throw new ArgumentNullException(nameof(cacheKeyGenerator)); ResponseCache = responseCache ?? throw new ArgumentNullException(nameof(responseCache)); CacheDeterminer = cacheDeterminer ?? throw new ArgumentNullException(nameof(cacheDeterminer)); Duration = Checks.ThrowIfDurationTooSmall(RequiredMetadata <IResponseDurationMetadata>().Duration); DurationMilliseconds = Duration * 1000; var executingLockMetadata = Metadata <IExecutingLockMetadata>(); LockMillisecondsTimeout = Checks.ThrowIfLockMillisecondsTimeoutInvalid(executingLockMetadata?.LockMillisecondsTimeout ?? options.DefaultLockMillisecondsTimeout).Value; Interceptors = interceptorAggregator; DumpStreamCapacity = Checks.ThrowIfDumpStreamInitialCapacityTooSmall(dumpStreamCapacity, nameof(dumpStreamCapacity)); OnCannotExecutionThroughLock = options.OnCannotExecutionThroughLock ?? DefaultExecutionLockTimeoutFallback.SetStatus429; OnExecutionLockTimeout = executingLockMetadata?.OnExecutionLockTimeout ?? options.OnExecutionLockTimeoutFallback ?? DefaultExecutionLockTimeoutFallback.SetStatus429; TMetadata?Metadata <TMetadata>() where TMetadata : class => metadatas.GetMetadata <TMetadata>();
/// <inheritdoc/> public IFilterMetadata CreateFilter(IServiceProvider serviceProvider) { if (serviceProvider is null) { throw new ArgumentNullException(nameof(serviceProvider)); } var endpointAccessor = serviceProvider.GetRequiredService <IEndpointAccessor>(); var buildContext = new FilterBuildContext(serviceProvider, endpointAccessor); if (!buildContext.Options.Enable) { return(EmptyFilterMetadata.Instance); } IResponseCache responseCache = GetResponseCache(buildContext); ICacheKeyGenerator cacheKeyGenerator = TryGetCustomCacheKeyGenerator(buildContext, out FilterType filterType) ?? CreateCacheKeyGenerator(buildContext, out filterType); var cacheDeterminer = serviceProvider.GetRequiredService <IResponseCacheDeterminer>(); var cachingDiagnosticsAccessor = serviceProvider.GetRequiredService <CachingDiagnosticsAccessor>(); var interceptorAggregator = new InterceptorAggregator(GetCachingProcessInterceptor(buildContext)); var lockMode = buildContext.GetMetadata <IExecutingLockMetadata>()?.LockMode ?? buildContext.Options.DefaultExecutingLockMode; lockMode = Checks.ThrowIfExecutingLockModeIsNone(lockMode); var dumpStreamCapacity = buildContext.GetMetadata <IDumpStreamInitialCapacityMetadata>()?.Capacity ?? ResponseCachingConstants.DefaultDumpCapacity; Checks.ThrowIfDumpStreamInitialCapacityTooSmall(dumpStreamCapacity, nameof(dumpStreamCapacity)); Debug.WriteLine("FilterType {0} for endpoint {1}", filterType, endpointAccessor); var executingLockPool = lockMode switch { ExecutingLockMode.ActionSingle => CreateSharedExecutingLockPool(serviceProvider), ExecutingLockMode.CacheKeySingle => CreateExclusiveExecutingLock(serviceProvider), _ => null, }; var responseCachingContext = new ResponseCachingContext(metadatas: endpointAccessor.Metadatas, cacheKeyGenerator: cacheKeyGenerator, responseCache: responseCache, cacheDeterminer: cacheDeterminer, options: buildContext.Options, interceptorAggregator: interceptorAggregator, dumpStreamCapacity: dumpStreamCapacity); return(filterType switch { FilterType.Resource => executingLockPool is null ? new DefaultResourceCacheFilter(responseCachingContext, cachingDiagnosticsAccessor) : new DefaultLockedResourceCacheFilter(responseCachingContext, executingLockPool, cachingDiagnosticsAccessor), FilterType.Action => executingLockPool is null ? new DefaultActionCacheFilter(responseCachingContext, cachingDiagnosticsAccessor) : new DefaultLockedActionCacheFilter(responseCachingContext, executingLockPool, cachingDiagnosticsAccessor), _ => throw new NotImplementedException($"Not ready to support FilterType: {filterType}"), });
/// <summary> /// Force re-evaluates the given aggregator with the given interceptors. /// </summary> private void PopulateAggregator(InterceptorAggregator <TInterceptor> aggregator, TInterceptor[] interceptors) { ReflectionCache.SetInterceptors(aggregator, interceptors); }