| File: System\Composition\Hosting\Core\ExportDescriptorRegistryUpdate.cs | Web Access |
| Project: src\runtime\src\libraries\System.Composition.Hosting\src\System.Composition.Hosting.csproj (System.Composition.Hosting) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; namespace System.Composition.Hosting.Core { internal sealed class ExportDescriptorRegistryUpdate : DependencyAccessor { private readonly IDictionary<CompositionContract, ExportDescriptor[]> _partDefinitions; private readonly ExportDescriptorProvider[] _exportDescriptorProviders; private readonly Dictionary<CompositionContract, UpdateResult> _updateResults = new Dictionary<CompositionContract, UpdateResult>(); private static readonly CompositionDependency[] s_noDependenciesValue = Array.Empty<CompositionDependency>(); private static readonly Func<CompositionDependency[]> s_noDependencies = () => s_noDependenciesValue; private bool _updateFinished; public ExportDescriptorRegistryUpdate( IDictionary<CompositionContract, ExportDescriptor[]> partDefinitions, ExportDescriptorProvider[] exportDescriptorProviders) { _partDefinitions = partDefinitions; _exportDescriptorProviders = exportDescriptorProviders; } public void Execute(CompositionContract contract) { // Opportunism - we'll miss recursive calls to Execute(), but this will catch some problems // and the _updateFinished flag is required for other purposes anyway. if (_updateFinished) throw new InvalidOperationException(SR.Update_already_executed); CompositionDependency initial; if (TryResolveOptionalDependency("initial request", contract, true, out initial)) { var @checked = new HashSet<ExportDescriptorPromise>(); var checking = new Stack<CompositionDependency>(); CheckTarget(initial, @checked, checking); } _updateFinished = true; foreach (var result in _updateResults) { var resultContract = result.Key; var descriptors = result.Value.GetResults().Select(cb => cb.GetDescriptor()).ToArray(); _partDefinitions.Add(resultContract, descriptors); } } private void CheckTarget(CompositionDependency dependency, HashSet<ExportDescriptorPromise> @checked, Stack<CompositionDependency> checking) { if (dependency.IsError) { var message = new StringBuilder(); dependency.DescribeError(message); message.AppendLine(); message.Append(DescribeCompositionStack(dependency, checking)); var ex = new CompositionFailedException(message.ToString()); Debug.WriteLine(SR.Diagnostic_ThrowingException, ex.ToString()); throw ex; } if (@checked.Contains(dependency.Target)) return; @checked.Add(dependency.Target); checking.Push(dependency); foreach (var dep in dependency.Target.Dependencies) CheckDependency(dep, @checked, checking); checking.Pop(); } private void CheckDependency(CompositionDependency dependency, HashSet<ExportDescriptorPromise> @checked, Stack<CompositionDependency> checking) { if (@checked.Contains(dependency.Target)) { var sharedSeen = false; var nonPrereqSeen = !dependency.IsPrerequisite; foreach (var step in checking) { if (step.Target.IsShared) sharedSeen = true; if (sharedSeen && nonPrereqSeen) break; if (step.Target.Equals(dependency.Target)) { var message = new StringBuilder(); message.AppendFormat(SR.ExportDescriptor_UnsupportedCycle, dependency.Target.Origin); message.AppendLine(); message.Append(DescribeCompositionStack(dependency, checking)); var ex = new CompositionFailedException(message.ToString()); Debug.WriteLine(SR.Diagnostic_ThrowingException, ex.ToString()); throw ex; } if (!step.IsPrerequisite) nonPrereqSeen = true; } } CheckTarget(dependency, @checked, checking); } private static StringBuilder DescribeCompositionStack(CompositionDependency import, Stack<CompositionDependency> dependencies) { var result = new StringBuilder(); if (dependencies.Count == 0 || dependencies.Peek() == null) { return result; } foreach (CompositionDependency step in dependencies) { result.AppendFormat(SR.ExportDescriptor_DependencyErrorLine, import.Site, step.Target.Origin); result.AppendLine(); import = step; } result.AppendFormat(SR.ExportDescriptor_DependencyErrorContract, import.Contract); return result; } protected override IEnumerable<ExportDescriptorPromise> GetPromises(CompositionContract contract) { if (_updateFinished) { throw new Exception(SR.Dependencies_Should_Be_Requested_Earlier); } ExportDescriptor[] definitions; if (_partDefinitions.TryGetValue(contract, out definitions)) return definitions.Select(d => new ExportDescriptorPromise(contract, "Preexisting", false, s_noDependencies, _ => d)).ToArray(); UpdateResult updateResult; if (!_updateResults.TryGetValue(contract, out updateResult)) { updateResult = new UpdateResult(_exportDescriptorProviders); _updateResults.Add(contract, updateResult); } ExportDescriptorProvider nextProvider; while (updateResult.TryDequeueNextProvider(out nextProvider)) { var newDefinitions = nextProvider.GetExportDescriptors(contract, this); foreach (var definition in newDefinitions) updateResult.AddPromise(definition); } return updateResult.GetResults(); } } }