File: System\ComponentModel\Composition\Hosting\ImportEngine.PartManager.cs
Web Access
Project: src\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());
                }
            }
        }
    }
}