| File: System\ComponentModel\Composition\Hosting\ImportEngine.PartManager.cs | Web Access |
| Project: src\runtime\src\libraries\System.ComponentModel.Composition\src\System.ComponentModel.Composition.csproj (System.ComponentModel.Composition) |
// 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.ComponentModel.Composition.Primitives; using System.Linq; using Microsoft.Internal.Collections; namespace System.ComponentModel.Composition.Hosting { public partial class ImportEngine { /// <summary> /// Used by the <see cref="ImportEngine"/> to manage the composition of a given part. /// It stores things like the list of disposable exports used to satisfy the imports as /// well as the caching of the exports discovered during previewing of a part. /// </summary> private sealed class PartManager { private Dictionary<ImportDefinition, List<IDisposable>>? _importedDisposableExports; private Dictionary<ImportDefinition, Export[]?>? _importCache; private string[]? _importedContractNames; private readonly ComposablePart _part; private ImportState _state = ImportState.NoImportsSatisfied; private readonly ImportEngine _importEngine; public PartManager(ImportEngine importEngine, ComposablePart part) { _importEngine = importEngine; _part = part; } public ComposablePart Part { get { return _part; } } public ImportState State { get { using (_importEngine._lock.LockStateForRead()) { return _state; } } set { using (_importEngine._lock.LockStateForWrite()) { _state = value; } } } public bool TrackingImports { get; set; } public IEnumerable<string> GetImportedContractNames() { if (Part == null) { return Enumerable.Empty<string>(); } return _importedContractNames ??= Part.ImportDefinitions.Select(import => import.ContractName ?? ImportDefinition.EmptyContractName).Distinct().ToArray(); } public CompositionResult TrySetImport(ImportDefinition import, Export[] exports) { try { Part.SetImport(import, exports); UpdateDisposableDependencies(import, exports); return CompositionResult.SucceededResult; } catch (CompositionException ex) { // Pulling on one of the exports failed return new CompositionResult( ErrorBuilder.CreatePartCannotSetImport(Part, import, ex)); } catch (ComposablePartException ex) { // Type mismatch between export and import return new CompositionResult( ErrorBuilder.CreatePartCannotSetImport(Part, import, ex)); } } public void SetSavedImport(ImportDefinition import, Export[]? exports, AtomicComposition? atomicComposition) { if (atomicComposition != null) { var savedExports = GetSavedImport(import); // Add a revert action to revert the stored exports // in the case that this atomicComposition gets rolled back. atomicComposition.AddRevertAction(() => SetSavedImport(import, savedExports, null)); } _importCache ??= new Dictionary<ImportDefinition, Export[]?>(); _importCache[import] = exports; } public Export[]? GetSavedImport(ImportDefinition import) { Export[]? exports = null; // We don't care about the return value we just want the exports // and if it isn't present we just return the initialized null value _importCache?.TryGetValue(import, out exports); return exports; } public void ClearSavedImports() { _importCache = null; } public CompositionResult TryOnComposed() { try { Part.Activate(); return CompositionResult.SucceededResult; } catch (ComposablePartException ex) { // Type failed to be constructed, imports could not be set, etc return new CompositionResult( ErrorBuilder.CreatePartCannotActivate(Part, ex)); } } public void UpdateDisposableDependencies(ImportDefinition import, Export[] exports) { // Determine if there are any new disposable exports, optimizing for the most // likely case, which is that there aren't any List<IDisposable>? disposableExports = null; foreach (var export in exports) { if (export is IDisposable disposableExport) { disposableExports ??= new List<IDisposable>(); disposableExports.Add(disposableExport); } } // Dispose any existing references previously set on this import List<IDisposable>? oldDisposableExports = null; if (_importedDisposableExports != null && _importedDisposableExports.TryGetValue(import, out oldDisposableExports)) { oldDisposableExports.ForEach(disposable => disposable.Dispose()); // If there aren't any replacements, get rid of the old storage if (disposableExports == null) { _importedDisposableExports.Remove(import); if (_importedDisposableExports.Count == 0) { _importedDisposableExports = null; } return; } } // Record the new collection if (disposableExports != null) { _importedDisposableExports ??= new Dictionary<ImportDefinition, List<IDisposable>>(); _importedDisposableExports[import] = disposableExports; } } public void DisposeAllDependencies() { if (_importedDisposableExports != null) { IEnumerable<IDisposable> dependencies = _importedDisposableExports.Values .SelectMany(exports => exports); _importedDisposableExports = null; dependencies.ForEach(disposableExport => disposableExport.Dispose()); } } } } }