protected override Task <DependencyResult <TDependencyInstance> > ResolveNewDependencyInstanceAsync(IDependencyResolutionContext context) { try { var specs = context.EnsureNotNull(nameof(context)).Value.Specs; // var factoryArgs = ReadDA(ref _factoryArgs); if (factoryArgs is null) { // Поскольку в данном обработчике явно не заданы параметры конструктора нового экземпляра, используются те, что указаны в спецификации, если они подходят для использования (см. код ниже). // if ((factoryArgs = specs.NewInstanceFactoryArgs as IArgsTuple <TArg1>) == null || factoryArgs.ArgsCount != __ArgsCount) { return(TaskUtilities.FromResult(DependencyResult <TDependencyInstance> .None)); } } else if (!(specs.NewInstanceFactoryArgs is null)) { // Поскольку спецификацией явно определены параметры конструктора нового экземпляра, а также явно заданны в данном обработчике (factoryArgs != null), то получается конфликт. // return(TaskUtilities.FromResult(DependencyResult <TDependencyInstance> .None)); } return (TaskUtilities .FromResult( result: new DependencyResult <TDependencyInstance>( instance: ReadDA(ref _getDependencyCtor)()(factoryArgs.Arg1), isNewInstance: true))); } catch (Exception exception) { return(Task.FromException <DependencyResult <TDependencyInstance> >(exception)); } }
protected override Task <DependencyResult <TDependencyInstance> > ResolveNewDependencyInstanceAsync(IDependencyResolutionContext context) { try { var specs = context.EnsureNotNull(nameof(context)).Value.Specs; // var factoryArgs = ReadDA(ref _factoryArgs); if (factoryArgs is null) { if ((factoryArgs = specs.NewInstanceFactoryArgs as IArgsTuple <TArg1, TArg2, TArg3, TArg4, TArg5>) == null || factoryArgs.ArgsCount != __ArgsCount) { return(TaskUtilities.FromResult(DependencyResult <TDependencyInstance> .None)); } } else if (specs.NewInstanceFactoryArgs != null) { return(TaskUtilities.FromResult(DependencyResult <TDependencyInstance> .None)); } // return (TaskUtilities .FromResult( result: new DependencyResult <TDependencyInstance>( instance: ReadDA(ref _getDependencyCtor) () (factoryArgs.Arg1, factoryArgs.Arg2, factoryArgs.Arg3, factoryArgs.Arg4, factoryArgs.Arg5), isNewInstance: true))); } catch (Exception exception) { return(Task.FromException <DependencyResult <TDependencyInstance> >(exception)); } }
protected override Task <DependencyResult <TDependencyInstance> > ResolveNewDependencyInstanceAsync(IDependencyResolutionContext context) { try { var specs = context.EnsureNotNull(nameof(context)).Value.Specs; // if ((specs.NewInstanceFactoryArgs?.ArgsCount ?? 0) != __ArgsCount) { return(TaskUtilities.FromResult(DependencyResult <TDependencyInstance> .None)); } else { return (TaskUtilities .FromResult( result: new DependencyResult <TDependencyInstance>( instance: ReadDA(ref _getDependencyCtor, considerDisposeRequest: true)()(), isNewInstance: true))); } } catch (Exception exception) { return(Task.FromException <DependencyResult <TDependencyInstance> >(exception)); } }
// TODO: Put strings into the resources. // public virtual DependencyResult ExecuteResolution(IDependencyResolutionContext resolutionCtx) { resolutionCtx.EnsureNotNull(nameof(resolutionCtx)); // if (resolutionCtx.Specs.IsNewInstanceRequired) { return(DependencyResult.None); } else { var scope = _hasServiceScope ? ServiceScope : null; var sp = scope is null ? ServiceProvider : scope.ServiceProvider; var instance = rethrowSpException(locOp: locSp => locSp.GetService(serviceType: resolutionCtx.Specs.DependencyType), locSp: sp, locScope: scope); if (instance is null) { return(DependencyResult.None); } else { return(new DependencyResult(instance: instance, isNewInstance: false)); } } // object rethrowSpException(Func <IServiceProvider, object> locOp, IServiceProvider locSp, IServiceScope locScope) { try { return(locOp(arg: locSp)); } catch (Exception exception) { throw new EonException( message: $"The service provider has thrown an exception while handling the dependency resolution request.{Environment.NewLine}Service provider:{locSp.FmtStr().GNLI2()}{(locScope is null ? string.Empty : $"{Environment.NewLine}Service scope:{locScope.FmtStr().GNLI2()}")}{Environment.NewLine}Component:{this.FmtStr().GNLI2()}", innerException: exception) .SetErrorCode(code: DependencyErrorCodes.Fault); } }
protected override async Task <DependencyResult <TDependencyInstance> > ResolveDependencyAsync(IDependencyResolutionContext ctx) { var specs = ctx.EnsureNotNull(nameof(ctx)).Value.Specs; // if (specs.DependencyType.IsAssignableFrom(DependencyInstanceType)) { var newInstanceResult = default(DependencyResult <TDependencyInstance>); if (specs.IsNewInstanceRequired || !CanShareDependency) { try { newInstanceResult = await ResolveNewDependencyInstanceAsync(ctx : ctx).ConfigureAwait(false); if (newInstanceResult.Instance is null) { return(newInstanceResult); } else if (!newInstanceResult.IsNewInstance) { throw new EonException( message: $"Результат вызова метода '{nameof(ResolveNewDependencyInstanceAsync)}' не соответствует ожидаемому. В частности, свойство результата '{nameof(newInstanceResult)}.{nameof(newInstanceResult.IsNewInstance)}' не соответствует ожидаемому значение '{bool.TrueString}'."); } else if (specs.PreventNewInstanceInitialization) { return(newInstanceResult); } else { await P_InitializeNewDependencyInstanceAsync(instance : newInstanceResult.Instance).ConfigureAwait(false); return(newInstanceResult); } } catch (Exception exception) { if (newInstanceResult.IsNewInstance) { (newInstanceResult.Instance as IDisposable)?.Dispose(exception); } throw; } } else { // В спецификации запроса отсутствует требование разрешения зависимости именно в новый экземпляр (см. specs.IsNewInstanceRequired) + обработчик может "шарить" экземпляр (см. CanShareDependency). // var sharedInstanceStore = ReadDA(ref _sharedInstanceStore); if (sharedInstanceStore is null) { var lck = ReadDA(ref _sharedInstanceInitializationLock); var lckAcquired = false; try { lckAcquired = await lck.WaitAsync(millisecondsTimeout : TaskUtilities.DefaultAsyncTimeoutMilliseconds).ConfigureAwait(false); if (!lckAcquired) { throw new LockAcquisitionFailException(reason: LockAcquisitionFailReason.TimeoutElapsed); } // sharedInstanceStore = ReadDA(ref _sharedInstanceStore); if (sharedInstanceStore is null) { try { newInstanceResult = await ResolveNewDependencyInstanceAsync(ctx : ctx).ConfigureAwait(false); if (newInstanceResult.Instance is null) { WriteDA(location: ref _sharedInstanceStore, value: sharedInstanceStore = default(TDependencyInstance).ToValueHolder(ownsValue: false)); } else if (!newInstanceResult.IsNewInstance) { throw new EonException( message: $"Результат вызова метода '{nameof(ResolveNewDependencyInstanceAsync)}' не соответствует ожидаемому. В частности, свойство результата '{nameof(newInstanceResult)}.{nameof(newInstanceResult.IsNewInstance)}' не соответствует ожидаемому значение '{bool.TrueString}'."); } else if (specs.PreventNewInstanceInitialization) { WriteDA( location: ref _sharedInstanceStore, value: sharedInstanceStore = newInstanceResult.Instance.ToValueHolder(ownsValue: true)); } else { try { await P_InitializeNewDependencyInstanceAsync(instance : newInstanceResult.Instance).ConfigureAwait(false); sharedInstanceStore = newInstanceResult.Instance.ToValueHolder(ownsValue: true); } catch (Exception exception) { sharedInstanceStore = new Vh <TDependencyInstance>(exception: exception); } WriteDA(location: ref _sharedInstanceStore, value: sharedInstanceStore); } } catch (Exception exception) { if (newInstanceResult.IsNewInstance && !ReferenceEquals(itrlck.Get(ref _sharedInstanceStore)?.ValueDisposeTolerant, newInstanceResult.Instance)) { (newInstanceResult.Instance as IDisposable)?.Dispose(exception); } throw; } } } finally { if (lckAcquired) { try { lck.Release(); } catch (ObjectDisposedException) { } } } } if (sharedInstanceStore.Value is null) { return(DependencyResult <TDependencyInstance> .None); } else { return(new DependencyResult <TDependencyInstance>(instance: sharedInstanceStore.Value, isNewInstance: false)); } } } else { return(DependencyResult <TDependencyInstance> .None); } }
// TODO: Put strings into the resources. // public virtual bool ResolveDependency <TDependency>(IDependencyResolutionContext ctx, out TDependency dependency) where TDependency : class { // ctx.EnsureNotNull(nameof(ctx)); if (!ReferenceEquals(ctx.Scope, this)) { throw new ArgumentOutOfRangeException( paramName: nameof(ctx), message: $"Указанный контекст не принадлежит данной области функциональной зависимости.{Environment.NewLine}\tКонтекст:{ctx.FmtStr().GNLI2()}{Environment.NewLine}\tОбласть:{this.FmtStr().GNLI2()}"); } else if (!typeof(TDependency).IsAssignableFrom(ctx.Specs.DependencyType)) { throw new ArgumentOutOfRangeException( paramName: nameof(ctx), message: $"Тип функциональной зависимости '{ctx.Specs.DependencyType}', определенный параметрами указанного контекста '{ctx}', не совместим с типом функциональной зависимости '{typeof(TDependency)}', указанным для данного вызова разрешения функциональной зависимости."); } // if (ctx.Specs.IsNewInstanceRequired && _prohibitNewInstanceRequest) { throw new EonException(message: $"Для данной области функциональной зависимости запрещены запросы на разрешение зависимости в новый экземпляр.{Environment.NewLine}\tОбласть:{this.FmtStr().GNLI2()}"); } #if DO_NOT_USE_EON_LOGGING_API if (ctx.IsAdvancedLoggingEnabled) { throw new NotSupportedException(message: $"В тек. версии расширенное логирование разрешения функциональной зависимости (см. {nameof(ctx)}.{nameof(ctx.IsAdvancedLoggingEnabled)}) не поддерживается.").SetErrorCode(code: GeneralErrorCodes.Operation.NotSupported); } #else var isContextAdvancedLoggingEnabled = ctx.IsAdvancedLoggingEnabled; #endif // var ensureResolution = ctx.Specs.EnsureResolution; var isNewInstanceRequired = ctx.Specs.IsNewInstanceRequired; var selectCriterion = ctx.Specs.SelectCriterion; var errorMessagePrologue = $"Было затребовано разрешение функциональной зависимости.{Environment.NewLine}Функциональная зависимость:{Environment.NewLine}{typeof(TDependency).FmtStr().G().IndentLines()}{Environment.NewLine}Параметры разрешения:{Environment.NewLine}{ctx.Specs.ToString().IndentLines()}."; // List <IDependencyHandler2> currentScopeHandlersTraversal; IDependencyHandler2 currentHandler; object resultDependencyInstanceAsObject; IDisposable resultInstanceAsDisposable; var resultInstance = default(TDependency); var handlerResult = DependencyResult.None; // var newDependencyInstancesDisposeRegistry = ctx.Specs.DisposeRegistry ?? ReadDA(ref _newDependencyInstancesDisposeRegistry); // var runningResolutionsSpinLock = ReadDA(ref _runningResolutionsSpinLock); var runningResolutionsUniqueness = ReadDA(ref _runningResolutionsUniqueness); var runningResolutionsSequence = ReadDA(ref _runningResolutionsSequence); // var involvedScopes = new HashSet <IDependencyScope>(comparer: ReferenceEqualityComparer <IDependencyScope> .Instance); try { // Регистрация выполнения запроса ф. зависимости. // runningResolutionsSpinLock .Invoke( () => { EnsureNotDisposeState(); // if (runningResolutionsSequence.Count >= MaxCountOfRunningResolutions) { throw new EonException( message: $"{errorMessagePrologue}{Environment.NewLine}Для области функциональной зависимости достигнут предел '{MaxCountOfRunningResolutions}' максимального количества одновременно выполняющихся вызовов разрешения функциональной зависимости.{Environment.NewLine}\tОбласть функциональной зависимости:{this.FmtStr().GNLI2()}") .SetErrorCode(DependencyErrorCodes.Fault); } else if (runningResolutionsUniqueness.Add(item: ctx)) { runningResolutionsSequence.Add(item: ctx); } else { throw new EonException( message: $"{errorMessagePrologue}{Environment.NewLine}Указанный контекст уже используется другим вызовом разрешения функциональной зависимости в данной области функциональной зависимости.{Environment.NewLine}\tКонтекст:{ctx.FmtStr().GNLI2()}{Environment.NewLine}\tОбласть функциональной зависимости:{this.FmtStr().GNLI2()}") .SetErrorCode(DependencyErrorCodes.Fault); } }); // var thisScope = (IDependencyScope)this; var previousScope = default(IDependencyScope); foreach (var currentScope in thisScope.TreeNode().SelfAndAncestors(parentSelector: locItem => locItem.GetOuterScope())) { if (!involvedScopes.Add(currentScope)) { throw new EonException( message: $"{errorMessagePrologue}{Environment.NewLine}Указанная область функциональной зависимости в составе окружающих (внешних) областей для данной области повторяется, что недопустимо.{Environment.NewLine}\tДанная область:{this.FmtStr().GNLI2()}{Environment.NewLine}\tУказанная область:{this.FmtStr().GNLI2()}{Environment.NewLine}\tОбласть, предшествующая указанной:{previousScope.FmtStr().GNLI2()}") .SetErrorCode(DependencyErrorCodes.Fault); } // Составить упорядоченный список обхода обработчиков запроса ф. зависимости. // currentScopeHandlersTraversal = new List <IDependencyHandler2>(collection: currentScope.GetResolutionModel()); if (involvedScopes.Count == 1) { // Сначала должны быть использованы обработчики запроса ф. зависимости первичной модели (если она задана), поэтому они добавляются в начало списка обхода. // var primaryModel = ctx.Specs.PrimaryResolutionModel.EmptyIfNull(); if (currentScopeHandlersTraversal.Count == 0) { currentScopeHandlersTraversal.AddRange(collection: primaryModel); } else { currentScopeHandlersTraversal.InsertRange(index: 0, collection: primaryModel); } // previousScope = currentScope; } // Обход списка обработчиков запроса ф. зависимости. // var currentHandlerIndex = 0; var handlerRedirectCounter = 0; for (; resultInstance is null && currentHandlerIndex < currentScopeHandlersTraversal.Count;) { currentHandler = currentScopeHandlersTraversal[currentHandlerIndex]; // if (currentHandler != null && ctx.TryInvolveHandler(handler: currentHandler)) { resultInstanceAsDisposable = (resultDependencyInstanceAsObject = (handlerResult = currentHandler.ExecuteResolution(resolutionCtx: ctx)).Instance) as IDisposable; if (resultDependencyInstanceAsObject is null) { if (!(handlerResult.RedirectHandler is null)) { if (handlerRedirectCounter++ < DefaultOfMaxCountOfResolutionRedirect) { var redirectHandlerIndex = currentHandlerIndex + 1; if (redirectHandlerIndex < currentScopeHandlersTraversal.Count) { currentScopeHandlersTraversal.Insert(index: redirectHandlerIndex, item: handlerResult.RedirectHandler); } else { currentScopeHandlersTraversal.Add(item: handlerResult.RedirectHandler); } } else { throw new EonException( message: $"Dependency resolution was broken due to the count of dependency handlers redirects reached the limit — {DefaultOfMaxCountOfResolutionRedirect:d} (see {nameof(DependencyScope)}.{nameof(DefaultOfMaxCountOfResolutionRedirect)}).{Environment.NewLine}Redirect from handler:{currentHandler.FmtStr().GNLI2()}{Environment.NewLine}Redirect to handler:{handlerResult.RedirectHandler.FmtStr().GNLI2()}{Environment.NewLine}Scope:{currentScope.FmtStr().GNLI2()}") .SetErrorCode(code: DependencyErrorCodes.Fault); } } } else if ((resultInstance = resultDependencyInstanceAsObject as TDependency) is null) { throw new EonException( $"{errorMessagePrologue}{Environment.NewLine}Компонент, исполняющий разрешение функциональной зависимости, возвратил недопустимый результат. Тип '{resultDependencyInstanceAsObject.GetType()}' экземпляра, полученного от компонента, не совместим с требуемым типом '{typeof(TDependency)}'.{Environment.NewLine}\tКомпонент:{currentHandler.FmtStr().GNLI2()}") .SetErrorCode(DependencyErrorCodes.Fault); } else if (ctx.Specs.IsNewInstanceRequired && !handlerResult.IsNewInstance) { throw new EonException( $"{errorMessagePrologue}{Environment.NewLine}Компонент, исполняющий разрешение функциональной зависимости, возвратил недопустимый результат. От компонента было затребовано создание нового экземпляра, но компонент возвратил результат, указывающий, что экземпляр не является новым.{Environment.NewLine}\tКомпонент:{currentHandler.FmtStr().GNLI2()}") .SetErrorCode(DependencyErrorCodes.Fault); } else if (!ctx.IsMatchSelectCriterion(resultInstance)) { if (handlerResult.IsNewInstance) { resultInstanceAsDisposable?.Dispose(); } resultInstanceAsDisposable = null; resultDependencyInstanceAsObject = null; resultInstance = null; } else if (handlerResult.IsNewInstance && resultInstanceAsDisposable != null) { newDependencyInstancesDisposeRegistry.Register(resultInstanceAsDisposable); } } // #if !DO_NOT_USE_EON_LOGGING_API if (isContextAdvancedLoggingEnabled && !(resultDependencyInstance is null)) { string loggingInformationMessage; using (var acquiredBuffer = StringBuilderUtilities.AcquireBuffer()) { var sb = acquiredBuffer.StringBuilder; sb.AppendLine($"Функциональная зависимость разрешена."); sb.AppendLine($"\tПараметры разрешения:"); sb.AppendLine(ctx.Specs.ToString().IndentLines(indentSize: 2)); sb.AppendLine($"\tЭкземпляр зависимости:"); sb.Append(resultDependencyInstance.ToString().IndentLines(indentSize: 2)); loggingInformationMessage = sb.ToString(); } context.IssueInformation($"Разрешение функциональной зависимости (ИД контекста '{contextSequentialId:d}').", loggingInformationMessage); } #endif // currentHandlerIndex++; } // if (resultInstance != null) { break; } } } catch (Exception exception) { if (handlerResult.IsNewInstance) { DisposableUtilities.DisposeMany(exception, handlerResult.Instance as IDisposable); } throw; } finally { runningResolutionsSpinLock .Invoke( () => { if (!(Disposing || IsDisposed)) { runningResolutionsSequence.RemoveAll(match: locItem => ReferenceEquals(objA: locItem, objB: ctx)); runningResolutionsUniqueness.Remove(item: ctx); } }); } // if (resultInstance is null) { if (ensureResolution) { // Формирование текста сообщения ошибки. // // TODO: involvedExecutorsChainShowReportMaxCount сделать конфигурируемым. // var involvedExecutorsChainShowReportMaxCount = FormatStringUtilities.DefaultMaxCountOfCollectionOutputItems; string involvedExecutorsChainReport; IDependencyHandler2[] involvedHandlerChain; string involvedHandlerText(IDependencyHandler2 locHandler) => $"Объект:{locHandler.FmtStr().GNLI()}"; using (var buffer = EonStringBuilderUtilities.AcquireBuffer()) { var sb = buffer.StringBuilder; involvedHandlerChain = ctx.GetInvolvedHandlerChain(); var involvedExecutorsChainLimit = Math.Min(involvedExecutorsChainShowReportMaxCount, involvedHandlerChain.Length); for (var k = 0; k < involvedExecutorsChainLimit; k++) { if (k > 0) { sb.AppendLine(); } sb.Append($"{(k + 1):d}) {involvedHandlerText(involvedHandlerChain[ k ])}"); } if (involvedHandlerChain.Length > involvedExecutorsChainShowReportMaxCount) { if (involvedHandlerChain.Length - involvedExecutorsChainShowReportMaxCount < 3) { sb.Append($"{Environment.NewLine}{(involvedExecutorsChainShowReportMaxCount + 1):d}) {involvedHandlerText(involvedHandlerChain[ involvedExecutorsChainShowReportMaxCount ])}"); } else { sb.Append($"{Environment.NewLine}…"); } // if (involvedHandlerChain.Length > (involvedExecutorsChainShowReportMaxCount + 1)) { sb.Append($"{Environment.NewLine}{(involvedHandlerChain.Length):d}) {involvedHandlerText(involvedHandlerChain[ involvedHandlerChain.Length - 1 ])}"); } } involvedExecutorsChainReport = sb.ToString(); } throw new EonException( $"{errorMessagePrologue}{Environment.NewLine}Указанная зависимость не была разрешена.{Environment.NewLine}Детали:{Environment.NewLine}\tОбласть функциональной зависимости:{Environment.NewLine}{this.FmtStr().GI2()}{Environment.NewLine}\tПоследовательность задействованных обработчиков (длина {involvedHandlerChain.Length:d}):{Environment.NewLine}{involvedExecutorsChainReport.IndentLines2()}") .SetErrorCode(DependencyErrorCodes.Resolution_NotResolved); } else { dependency = null; return(false); } } else { dependency = resultInstance; return(true); } }