VsixCommunity / Community.VisualStudio.Toolkit.DependencyInjection

Adds Dependency Injection tooling for the Community.VisualStudio.Toolkit
Other
14 stars 8 forks source link

Same di service provider is registered with Visual Studio for all MicrosoftDIToolkitPackage derivations ! #13

Open tonyhallett opened 12 months ago

tonyhallett commented 12 months ago

[ProvideService(typeof(SToolkitServiceProvider<>), IsAsyncQueryable = true)]

https://github.com/VsixCommunity/Community.VisualStudio.Toolkit.DependencyInjection/blob/639e8340345a0d528670b70f7817af662d64c01a/src/Core/Shared/DIToolkitPackage.cs#L49

            // Add the IToolkitServiceProvider to the VS IServiceProvider
            AsyncServiceCreatorCallback serviceCreatorCallback = (sc, ct, t) =>
            {
                return Task.FromResult((object)this.ServiceProvider);
            };

            AddService(typeof(SToolkitServiceProvider<TPackage>), serviceCreatorCallback, true);

The generic does not work.

- Create two packages of this format. In one of them VS.GetRequiredService will throw as the IToolkitServiceProvider<TPackage> implementation will be for the other package.

    public sealed class DIBroken1Package : MicrosoftDIToolkitPackage<DIBroken1Package>
    {
        protected override void InitializeServices(IServiceCollection services)
        {
            services.RegisterCommands(ServiceLifetime.Singleton);
            services.AddSingleton<Dependency>();
        }

        internal static Dependency GetDependency()
        {
            var sp = VS.GetRequiredService<SToolkitServiceProvider<DIBroken1Package>, IToolkitServiceProvider<DIBroken1Package>>();
            return sp.GetRequiredService<Dependency>();
        }

    }

    internal class Dependency
    {
        public string Message { get; } = "Hello from Dependency";
    }

    [Command(PackageIds.MyCommand)]
    internal sealed class MyCommand : BaseDICommand
    {
        public MyCommand(DIToolkitPackage package):base(package)
        {
        }
        protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
        {
            var dependency = DIBroken1Package.GetDependency();
            await VS.MessageBox.ShowAsync(dependency.Message);
        }
    }

A quick and dirty solution

    public interface IToolkitServiceProviderContainer
    {
        IServiceProvider Get<TPackage>() where TPackage : AsyncPackage;
        IServiceProvider Get(Type packageType);
    }

    public class ToolkitServiceProviderContainer : IToolkitServiceProviderContainer
    {
        private static Dictionary<Type, IServiceProvider> _serviceProviders = new Dictionary<Type, IServiceProvider>();
        internal static void AddServiceProvider<TPackage>(IServiceProvider serviceProvider) where TPackage : AsyncPackage
        {
            _serviceProviders.Add(typeof(TPackage), serviceProvider);
        }

        public IServiceProvider Get<TPackage>() where TPackage : AsyncPackage
        {
            return this.Get(typeof(TPackage));
        }

        public IServiceProvider Get(Type packageType)
        {
            return _serviceProviders[packageType];
        }
    }

    [ProvideService(typeof(SToolkitServiceProviderContainer), IsAsyncQueryable = true)]
    public abstract class DIToolkitPackage<TPackage> : DIToolkitPackage
        where TPackage : AsyncPackage
    {
        /// <summary>
        /// Initializes the <see cref="AsyncPackage"/>
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <param name="progress"></param>
        /// <returns></returns>
        protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
        {
            // AS BEFORE

            this.ServiceProvider = serviceProvider;
            ToolkitServiceProviderContainer.AddServiceProvider<TPackage>(this.ServiceProvider);

            // Add the IToolkitServiceProvider to the VS IServiceProvider
            AsyncServiceCreatorCallback serviceCreatorCallback = (sc, ct, t) =>
            {
                if(t == typeof(SToolkitServiceProviderContainer))
                {
                    return Task.FromResult((object)new ToolkitServiceProviderContainer());
                }
                return Task.FromResult<object?>(null);

            };

            AddService(typeof(SToolkitServiceProviderContainer), serviceCreatorCallback, true);

           // AS BEFORE

       }

Alternatively, just make the ServiceProvider property static ?