diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/DependenciesTreeProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/DependenciesTreeProvider.cs index a7f5c3b21d0..4d875737753 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/DependenciesTreeProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/DependenciesTreeProvider.cs @@ -47,10 +47,14 @@ internal sealed partial class DependenciesTreeProvider : ProjectTreeProviderBase private readonly OrderPrecedenceImportCollection _removalActionHandlers; private readonly DependenciesSnapshotProvider _dependenciesSnapshotProvider; + private readonly IActiveConfiguredValue _activeConfiguredProjectExports; private readonly CancellationSeries _treeUpdateCancellationSeries; private readonly IProjectAccessor _projectAccessor; - private readonly ITaskDelayScheduler _debounce; + private readonly TaskDelayScheduler _debounce; + // NOTE we use a property import here as importing this via the constructor creates a loop between + // DependenciesTreeProvider and DependenciesTreeBuilder. A property import allows MEF to break that + // circular dependency. [Import] private DependenciesTreeBuilder TreeBuilder { get; set; } = null!; @@ -68,11 +72,13 @@ public DependenciesTreeProvider( IProjectThreadingService threadingService, IUnconfiguredProjectTasksService tasksService, IProjectAccessor projectAccessor, - DependenciesSnapshotProvider dependenciesSnapshotProvider) + DependenciesSnapshotProvider dependenciesSnapshotProvider, + IActiveConfiguredValue activeConfiguredProjectExports) : base(threadingService, unconfiguredProject) { _dependenciesSnapshotProvider = dependenciesSnapshotProvider; _projectAccessor = projectAccessor; + _activeConfiguredProjectExports = activeConfiguredProjectExports; _removalActionHandlers = new OrderPrecedenceImportCollection( ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, @@ -377,7 +383,7 @@ await _projectAccessor.OpenProjectForWriteAsync(ActiveConfiguredProject, project protected override ConfiguredProjectExports GetActiveConfiguredProjectExports(ConfiguredProject newActiveConfiguredProject) { - return GetActiveConfiguredProjectExports(newActiveConfiguredProject); + return (ConfiguredProjectExports)_activeConfiguredProjectExports.Value; } #region ITreeConstruction @@ -479,29 +485,25 @@ IProjectItemTree2 IProjectTreeOperations.NewTree(string caption, IProjectPropert /// /// Describes services collected from the active configured project. /// - [Export] - private sealed class MyConfiguredProjectExports : ConfiguredProjectExports + [Export(typeof(IConfiguredProjectExports))] + [method: ImportingConstructor] + private sealed class MyConfiguredProjectExports(ConfiguredProject configuredProject) + : ConfiguredProjectExports(configuredProject), + IConfiguredProjectExports { - [ImportingConstructor] - public MyConfiguredProjectExports(ConfiguredProject configuredProject) - : base(configuredProject) - { - } } /// /// A private implementation of for use with /// exports. /// - private sealed class ProjectDependencyTreeRemovalActionHandlerContext : IProjectTreeActionHandlerContext + private sealed class ProjectDependencyTreeRemovalActionHandlerContext(IProjectTreeProvider treeProvider) : IProjectTreeActionHandlerContext { - public IProjectTreeProvider TreeProvider { get; } + public IProjectTreeProvider TreeProvider { get; } = treeProvider; public IProjectTreeActionHandler SuccessorHandlerDelegator => throw new NotImplementedException(); - - public ProjectDependencyTreeRemovalActionHandlerContext(IProjectTreeProvider treeProvider) - { - TreeProvider = treeProvider; - } } + + // NOTE this interface is needed to work around accessiblity issues when making MyConfiguredProjectExports non-private + internal interface IConfiguredProjectExports { } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/ProjectImports/ImportTreeProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/ProjectImports/ImportTreeProvider.cs index e0867e82a2e..7c3aa01103b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/ProjectImports/ImportTreeProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/ProjectImports/ImportTreeProvider.cs @@ -45,6 +45,7 @@ internal sealed class ImportTreeProvider : ProjectTreeProviderBase, IProjectTree private readonly UnconfiguredProject _project; private readonly IActiveConfiguredProjectSubscriptionService _projectSubscriptionService; private readonly IUnconfiguredProjectTasksService _unconfiguredProjectTasksService; + private readonly IActiveConfiguredValue _activeConfiguredProjectExports; private DisposableBag? _subscriptions; private bool _showAllFiles; @@ -54,12 +55,14 @@ internal ImportTreeProvider( IProjectThreadingService threadingService, UnconfiguredProject project, IActiveConfiguredProjectSubscriptionService projectSubscriptionService, - IUnconfiguredProjectTasksService unconfiguredProjectTasksService) + IUnconfiguredProjectTasksService unconfiguredProjectTasksService, + IActiveConfiguredValue activeConfiguredProjectExports) : base(threadingService, project, useDisplayOrdering: true) { _project = project; _projectSubscriptionService = projectSubscriptionService; _unconfiguredProjectTasksService = unconfiguredProjectTasksService; + _activeConfiguredProjectExports = activeConfiguredProjectExports; } public override string? GetPath(IProjectTree node) @@ -346,9 +349,7 @@ void TearDownTree() protected override ConfiguredProjectExports GetActiveConfiguredProjectExports(ConfiguredProject newActiveConfiguredProject) { - Requires.NotNull(newActiveConfiguredProject); - - return GetActiveConfiguredProjectExports(newActiveConfiguredProject); + return (ConfiguredProjectExports)_activeConfiguredProjectExports.Value; } protected override void Dispose(bool disposing) @@ -361,13 +362,14 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - [Export] - private sealed class MyConfiguredProjectExports : ConfiguredProjectExports + [Export(typeof(IConfiguredProjectExports))] + [method: ImportingConstructor] + private sealed class MyConfiguredProjectExports(ConfiguredProject configuredProject) + : ConfiguredProjectExports(configuredProject), + IConfiguredProjectExports { - [ImportingConstructor] - public MyConfiguredProjectExports(ConfiguredProject configuredProject) - : base(configuredProject) - { - } } + + // NOTE this interface is needed to work around accessiblity issues when making MyConfiguredProjectExports non-private + internal interface IConfiguredProjectExports { } }