File: Workspace\Host\Mef\MefV1HostServices.cs
Web Access
Project: src\src\Workspaces\Core\Desktop\Microsoft.CodeAnalysis.Workspaces.Desktop.csproj (Microsoft.CodeAnalysis.Workspaces.Desktop)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Reflection;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Host.Mef
{
    /// <summary>
    /// A class that provides host services via classes instances exported via a MEF version 1 composition.
    /// </summary>
    public class MefV1HostServices : HostServices, IMefHostExportProvider
    {
        internal delegate MefV1HostServices CreationHook(IEnumerable<Assembly> assemblies);
 
        /// <summary>
        /// This delegate allows test code to override the behavior of <see cref="Create(IEnumerable{Assembly})"/>.
        /// </summary>
        /// <seealso cref="TestAccessor.HookServiceCreation"/>
        private static CreationHook s_CreationHook;
 
        // the export provider for the MEF composition
        private readonly ExportProvider _exportProvider;
 
        // accumulated cache for exports
        private ImmutableDictionary<ExportKey, IEnumerable> _exportsMap
            = ImmutableDictionary<ExportKey, IEnumerable>.Empty;
 
        private MefV1HostServices(ExportProvider exportProvider)
        {
            _exportProvider = exportProvider;
        }
 
        public static MefV1HostServices Create(ExportProvider exportProvider)
        {
            if (exportProvider == null)
            {
                throw new ArgumentNullException(nameof(exportProvider));
            }
 
            return new MefV1HostServices(exportProvider);
        }
 
        public static MefV1HostServices Create(IEnumerable<Assembly> assemblies)
        {
            if (assemblies == null)
            {
                throw new ArgumentNullException(nameof(assemblies));
            }
 
            if (s_CreationHook != null)
            {
                return s_CreationHook(assemblies);
            }
 
            var catalog = new AggregateCatalog(assemblies.Select(a => new AssemblyCatalog(a)));
            var container = new CompositionContainer(catalog, compositionOptions: CompositionOptions.DisableSilentRejection | CompositionOptions.IsThreadSafe);
            return new MefV1HostServices(container);
        }
 
        /// <summary>
        /// Creates a new <see cref="HostWorkspaceServices"/> associated with the specified workspace.
        /// </summary>
        protected internal override HostWorkspaceServices CreateWorkspaceServices(Workspace workspace)
        {
            return new MefWorkspaceServices(this, workspace);
        }
 
        /// <summary>
        /// Gets all the MEF exports of the specified type with the specified metadata.
        /// </summary>
        public IEnumerable<Lazy<TExtension, TMetadata>> GetExports<TExtension, TMetadata>()
        {
            var key = new ExportKey(typeof(TExtension).AssemblyQualifiedName, typeof(TMetadata).AssemblyQualifiedName);
            if (!_exportsMap.TryGetValue(key, out var exports))
            {
                exports = ImmutableInterlocked.GetOrAdd(ref _exportsMap, key, _ =>
                {
                    return _exportProvider.GetExports<TExtension, TMetadata>().ToImmutableArray();
                });
            }
 
            return (IEnumerable<Lazy<TExtension, TMetadata>>)exports;
        }
 
        /// <summary>
        /// Gets all the MEF exports of the specified type.
        /// </summary>
        public IEnumerable<Lazy<TExtension>> GetExports<TExtension>()
        {
            var key = new ExportKey(typeof(TExtension).AssemblyQualifiedName, "");
            if (!_exportsMap.TryGetValue(key, out var exports))
            {
                exports = ImmutableInterlocked.GetOrAdd(ref _exportsMap, key, _ =>
                    _exportProvider.GetExports<TExtension>().ToImmutableArray());
            }
 
            return (IEnumerable<Lazy<TExtension>>)exports;
        }
 
        internal TestAccessor GetTestAccessor()
            => new TestAccessor(this);
 
        private readonly struct ExportKey : IEquatable<ExportKey>
        {
            internal readonly string ExtensionTypeName;
            internal readonly string MetadataTypeName;
 
            public ExportKey(string extensionTypeName, string metadataTypeName)
            {
                this.ExtensionTypeName = extensionTypeName;
                this.MetadataTypeName = metadataTypeName;
                Hash.Combine(metadataTypeName.GetHashCode(), extensionTypeName.GetHashCode());
            }
 
            public bool Equals(ExportKey other)
            {
                return string.Compare(this.ExtensionTypeName, other.ExtensionTypeName, StringComparison.OrdinalIgnoreCase) == 0
                    && string.Compare(this.MetadataTypeName, other.MetadataTypeName, StringComparison.OrdinalIgnoreCase) == 0;
            }
 
            public override bool Equals(object obj)
                => obj is ExportKey exportKey && this.Equals(exportKey);
 
            public override int GetHashCode()
            {
                return Hash.Combine(this.MetadataTypeName.GetHashCode(), this.ExtensionTypeName.GetHashCode());
            }
        }
 
        internal readonly struct TestAccessor
        {
#pragma warning disable IDE0052 // Remove unread private members - hold onto the services
            private readonly MefV1HostServices _mefV1HostServices;
#pragma warning restore IDE0052 // Remove unread private members
 
            public TestAccessor(MefV1HostServices mefV1HostServices)
            {
                _mefV1HostServices = mefV1HostServices;
            }
 
            /// <summary>
            /// Injects replacement behavior for the <see cref="Create(IEnumerable{Assembly})"/> method.
            /// </summary>
            /// <param name="hook"></param>
            internal static void HookServiceCreation(CreationHook hook)
            {
                s_CreationHook = hook;
            }
        }
    }
}