File: Services\InProcServiceFactory.cs
Web Access
Project: src\src\Razor\src\Razor\src\Microsoft.VisualStudioCode.RazorExtension\Microsoft.VisualStudioCode.RazorExtension.csproj (Microsoft.VisualStudioCode.RazorExtension)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.CodeAnalysis.Remote.Razor;
using Microsoft.VisualStudio.Composition;
 
namespace Microsoft.VisualStudioCode.RazorExtension.Services;
 
/// <summary>
///  Creates Razor brokered services.
/// </summary>
/// <remarks>
///  This class holds <see cref="RazorBrokeredServiceBase.FactoryBase{TService}"/> instances in a static
///  field. This should work fine in tests, since brokered service factories are intended to be stateless.
///  However, if a factory is introduced that maintains state, this class will need to be revisited to
///  avoid holding onto state across tests.
/// </remarks>
internal static class InProcServiceFactory
{
    private static readonly Dictionary<Type, IInProcServiceFactory> s_factoryMap = BuildFactoryMap();
    private static ExportProvider? _exportProvider;
 
    private static Dictionary<Type, IInProcServiceFactory> BuildFactoryMap()
    {
        var result = new Dictionary<Type, IInProcServiceFactory>();
 
        foreach (var type in typeof(RazorBrokeredServiceBase.FactoryBase<>).Assembly.GetTypes())
        {
            if (!type.IsAbstract &&
                typeof(IInProcServiceFactory).IsAssignableFrom(type))
            {
                Assumes.True(typeof(RazorBrokeredServiceBase.FactoryBase<>) == type.BaseType!.GetGenericTypeDefinition());
 
                var genericType = type.BaseType.GetGenericArguments().Single();
 
                // ServiceHub requires a parameterless constructor, so we can safely rely on it existing too
                var factory = (IInProcServiceFactory)Activator.CreateInstance(type).AssumeNotNull();
                result.Add(genericType, factory);
            }
        }
 
        return result;
    }
 
    public static async Task<TService> CreateServiceAsync<TService>(
        VSCodeBrokeredServiceInterceptor brokeredServiceInterceptor, IWorkspaceProvider workspaceProvider, ILoggerFactory loggerFactory)
        where TService : class
    {
        Assumes.True(s_factoryMap.TryGetValue(typeof(TService), out var factory));
 
        var brokeredServiceData = new RazorBrokeredServiceData(ExportProvider: _exportProvider, loggerFactory, brokeredServiceInterceptor, workspaceProvider);
        var hostProvidedServices = new HostProvidedServices(brokeredServiceData);
 
        return (TService)await factory.CreateInProcAsync(hostProvidedServices).ConfigureAwait(false);
    }
 
    private class HostProvidedServices(RazorBrokeredServiceData brokeredServiceData) : IServiceProvider
    {
        public object? GetService(Type serviceType)
            => serviceType == typeof(RazorBrokeredServiceData) ? brokeredServiceData : null;
    }
 
    internal static class TestAccessor
    {
        public static void SetExportProvider(ExportProvider exportProvider)
        {
            _exportProvider = exportProvider;
        }
    }
}