@@ -52,12 +52,13 @@ public partial class PackageManagerControl : UserControl, IVsWindowSearch, IDisp
5252 private bool _initialized ;
5353 private IVsWindowSearchHost _windowSearchHost ;
5454 private IVsWindowSearchHostFactory _windowSearchHostFactory ;
55+ private ISourceRepositoryProvider _sourceRepositoryProvider ;
5556 private INuGetUILogger _uiLogger ;
5657 private readonly Guid _sessionGuid = Guid . NewGuid ( ) ;
5758 private Stopwatch _sinceLastRefresh ;
5859 private CancellationTokenSource _refreshCts ;
5960 // used to prevent starting new search when we update the package sources
60- // list in response to PackageSourcesChanged event .
61+ // list in response to Package Sources changing events .
6162 private bool _dontStartNewSearch ;
6263 // When executing a UI operation, we disable the PM UI and ignore any refresh requests.
6364 // This tells the operation execution part that it needs to trigger a refresh when done.
@@ -115,7 +116,7 @@ private async ValueTask InitializeAsync(PackageManagerModel model, INuGetUILogge
115116 var nuGetFeatureFlagService = await ServiceLocator . GetComponentModelServiceAsync < INuGetFeatureFlagService > ( ) ;
116117 var editorOptionsFactoryService = await ServiceLocator . GetComponentModelServiceAsync < IEditorOptionsFactoryService > ( ) ;
117118 NuGetExperimentationService = await ServiceLocator . GetComponentModelServiceAsync < INuGetExperimentationService > ( ) ;
118- ISourceRepositoryProvider sourceRepositoryProvider = await ServiceLocator . GetComponentModelServiceAsync < ISourceRepositoryProvider > ( ) ;
119+ _sourceRepositoryProvider = await ServiceLocator . GetComponentModelServiceAsync < ISourceRepositoryProvider > ( ) ;
119120 await NuGetUIThreadHelper . JoinableTaskFactory . SwitchToMainThreadAsync ( cancellationToken ) ;
120121
121122 _serviceBroker = model . Context . ServiceBroker ;
@@ -199,10 +200,7 @@ private async ValueTask InitializeAsync(PackageManagerModel model, INuGetUILogge
199200 controller . PackageManagerControl = this ;
200201 }
201202
202- List < SourceRepository > sourceRepositories = sourceRepositoryProvider . GetRepositories ( ) . ToList ( ) ;
203-
204- var auditSourceRepositories = Model . Context . SourceService . GetEnabledAuditSources ( ) ;
205- _packageVulnerabilityService = new PackageVulnerabilityService ( sourceRepositories , auditSourceRepositories , _uiLogger ) ;
203+ await SetVulnerabilityService ( _sourceRepositoryProvider ) ;
206204
207205 var solutionManager = Model . Context . SolutionManagerService ;
208206 solutionManager . ProjectAdded += OnProjectChanged ;
@@ -213,8 +211,6 @@ private async ValueTask InitializeAsync(PackageManagerModel model, INuGetUILogge
213211
214212 Model . Context . ProjectActionsExecuted += OnProjectActionsExecuted ;
215213
216- Model . Context . SourceService . PackageSourcesChanged += PackageSourcesChanged ;
217-
218214 Unloaded += PackageManagerUnloaded ;
219215
220216 if ( IsUILegalDisclaimerSuppressed ( ) )
@@ -227,10 +223,35 @@ private async ValueTask InitializeAsync(PackageManagerModel model, INuGetUILogge
227223 Settings . SettingsChanged += Settings_SettingsChanged ;
228224 }
229225
226+ private async Task SetVulnerabilityService ( ISourceRepositoryProvider sourceRepositoryProvider )
227+ {
228+ await TaskScheduler . Default ;
229+ List < SourceRepository > sourceRepositories = sourceRepositoryProvider . GetRepositories ( ) . ToList ( ) ;
230+ var auditSourceRepositories = Model . Context . SourceService . GetEnabledAuditSources ( ) ;
231+ var vulnerabilityService = new PackageVulnerabilityService ( sourceRepositories , auditSourceRepositories , _uiLogger ) ;
232+
233+ // Avoid concurrency issues by using the UI thread to synchronize reading and changing _packageVulnerabilityService.
234+ await NuGetUIThreadHelper . JoinableTaskFactory . SwitchToMainThreadAsync ( ) ;
235+ _packageVulnerabilityService = vulnerabilityService ;
236+ }
237+
230238 private void Settings_SettingsChanged ( object sender , EventArgs e )
231239 {
232- _detailModel . PackageSourceMappingViewModel . SettingsChanged ( ) ;
233- _detailModel . SetInstalledOrUpdateButtonIsEnabled ( ) ;
240+ // Set _dontStartNewSearch to true to prevent a new search started in
241+ // _sourceRepoList_SelectionChanged(). This method will start the new
242+ // search when needed by itself.
243+ _dontStartNewSearch = true ;
244+
245+ try
246+ {
247+ _detailModel . PackageSourceMappingViewModel . SettingsChanged ( ) ;
248+ _detailModel . SetInstalledOrUpdateButtonIsEnabled ( ) ;
249+ RefreshAfterSettingsChanged ( sender , e ) ;
250+ }
251+ finally
252+ {
253+ _dontStartNewSearch = false ;
254+ }
234255 }
235256
236257 public PackageRestoreBar RestoreBar { get ; private set ; }
@@ -604,50 +625,22 @@ private void ApplySettings(UserSettings settings, ISettings nugetSettings)
604625 }
605626 }
606627
607- private void PackageSourcesChanged ( object sender , IReadOnlyCollection < PackageSourceContextInfo > e )
628+ private void RefreshAfterSettingsChanged ( object sender , EventArgs e )
608629 {
609- // Set _dontStartNewSearch to true to prevent a new search started in
610- // _sourceRepoList_SelectionChanged(). This method will start the new
611- // search when needed by itself.
612- _dontStartNewSearch = true ;
613- TimeSpan timeSpan = GetTimeSinceLastRefreshAndRestart ( ) ;
614-
615- NuGetUIThreadHelper . JoinableTaskFactory . RunAsync ( ( ) => PackageSourcesChangedAsync ( e , timeSpan ) )
616- . PostOnFailure ( nameof ( PackageManagerControl ) , nameof ( PackageSourcesChanged ) ) ;
617- }
618-
619- private async Task PackageSourcesChangedAsync ( IReadOnlyCollection < PackageSourceContextInfo > packageSources , TimeSpan timeSpan )
620- {
621- try
630+ NuGetUIThreadHelper . JoinableTaskFactory . RunAsync ( async ( ) =>
622631 {
623- var sw = Stopwatch . StartNew ( ) ;
624- IReadOnlyCollection < PackageSourceMoniker > list = await PackageSourceMoniker . PopulateListAsync ( packageSources , CancellationToken . None ) ;
632+ await SetVulnerabilityService ( _sourceRepositoryProvider ) ;
625633
626634 await NuGetUIThreadHelper . JoinableTaskFactory . SwitchToMainThreadAsync ( ) ;
627- // We access UI components in these calls
628- PackageSourceMoniker prevSelectedItem = SelectedSource ;
635+ var settings = SaveSettings ( ) ;
629636
630- await PopulatePackageSourcesAsync ( list , optionalSelectSourceName : null , cancellationToken : CancellationToken . None ) ;
637+ // Re-initialize package sources first, since a change to them will affect package search results.
638+ await InitPackageSourcesAsync ( settings , CancellationToken . None ) ;
631639
632- // force a new search explicitly only if active source has changed
633- if ( prevSelectedItem == SelectedSource )
634- {
635- sw . Stop ( ) ;
636- EmitRefreshEvent ( timeSpan , RefreshOperationSource . PackageSourcesChanged , RefreshOperationStatus . NotApplicable , isUIFiltering : false , duration : sw . Elapsed . TotalMilliseconds ) ;
637- }
638- else
639- {
640- await RunAndEmitRefreshAsync ( async ( ) =>
641- {
642- SaveSettings ( ) ;
643- await SearchPackagesAndRefreshUpdateCountAsync ( useCacheForUpdates : false ) ;
644- } , RefreshOperationSource . PackageSourcesChanged , timeSpan , sw ) ;
645- }
646- }
647- finally
648- {
649- _dontStartNewSearch = false ;
650- }
640+ // Refresh package search results, which will also reflect any NuGet Audit settings changes.
641+ await SearchPackagesAndRefreshUpdateCountAsync ( useCacheForUpdates : false ) ;
642+ } )
643+ . PostOnFailure ( nameof ( PackageManagerControl ) , nameof ( RefreshAfterSettingsChanged ) ) ;
651644 }
652645
653646 private async Task IsCentralPackageManagementEnabledAsync ( CancellationToken cancellationToken )
@@ -703,7 +696,7 @@ private static string GetProjectSettingsKey(string projectName)
703696 // Save the settings of this doc window in the UIContext. Note that the settings
704697 // are not guaranteed to be persisted. We need to call Model.Context.SaveSettings()
705698 // to persist the settings.
706- public void SaveSettings ( )
699+ public UserSettings SaveSettings ( )
707700 {
708701 Assumes . NotNullOrEmpty ( _settingsKey ) ;
709702
@@ -724,6 +717,8 @@ public void SaveSettings()
724717 _packageDetail . _solutionView . SaveSettings ( settings ) ;
725718
726719 Model . Context . UserSettingsManager . AddSettings ( _settingsKey , settings ) ;
720+
721+ return settings ;
727722 }
728723
729724 private UserSettings LoadSettings ( )
@@ -1033,7 +1028,7 @@ private async ValueTask RefreshInstalledAndUpdatesTabsAsync()
10331028 Interlocked . Exchange ( ref _refreshCts , refreshCts ) ? . Cancel ( ) ;
10341029
10351030 // Update installed tab warning icon
1036- ( int vulnerablePackages , int deprecatedPackages ) = await GetInstalledVulnerableAndDeprecatedPackagesCountAsync ( loadContext , SelectedSource . PackageSources , refreshCts . Token ) ;
1031+ ( int vulnerablePackages , int deprecatedPackages ) = await GetInstalledVulnerableAndDeprecatedPackagesCountAsync ( loadContext , SelectedSource . PackageSources , _packageVulnerabilityService , refreshCts . Token ) ;
10371032 _topPanel . UpdateWarningStatusOnInstalledTab ( vulnerablePackages , deprecatedPackages ) ;
10381033
10391034 // Update updates tab count
@@ -1047,7 +1042,7 @@ private async ValueTask RefreshInstalledAndUpdatesTabsAsync()
10471042 _topPanel . UpdateCountOnUpdatesTab ( Model . CachedUpdates . Packages . Count ) ;
10481043 }
10491044
1050- private async Task < ( int , int ) > GetInstalledVulnerableAndDeprecatedPackagesCountAsync ( PackageLoadContext loadContext , IReadOnlyCollection < PackageSourceContextInfo > packageSources , CancellationToken token )
1045+ private async Task < ( int , int ) > GetInstalledVulnerableAndDeprecatedPackagesCountAsync ( PackageLoadContext loadContext , IReadOnlyCollection < PackageSourceContextInfo > packageSources , IPackageVulnerabilityService vulnerabilityService , CancellationToken token )
10511046 {
10521047 // Switch off the UI thread before fetching installed packages and deprecation metadata.
10531048 await TaskScheduler . Default ;
@@ -1060,7 +1055,7 @@ private async ValueTask RefreshInstalledAndUpdatesTabsAsync()
10601055 installedPackageCollection = PackageCollection . FromPackageReferences ( installedAndTransitivePackages . InstalledPackages ) ;
10611056 PackageCollection transitivePackageCollection = PackageCollection . FromPackageReferences ( installedAndTransitivePackages . TransitivePackages . Where ( p => p . TransitiveOrigins . Any ( ) ) ) ;
10621057 //Use ShutdownToken to ensure the operation is canceled if it's still running when VS shuts down.
1063- IEnumerable < PackageVulnerabilityMetadataContextInfo > [ ] transitivePackageVulnerabilities = await Task . WhenAll ( transitivePackageCollection . Select ( p => _packageVulnerabilityService . GetVulnerabilityInfoAsync ( p , VsShellUtilities . ShutdownToken ) ) ) ;
1058+ IEnumerable < PackageVulnerabilityMetadataContextInfo > [ ] transitivePackageVulnerabilities = await Task . WhenAll ( transitivePackageCollection . Select ( p => vulnerabilityService . GetVulnerabilityInfoAsync ( p , VsShellUtilities . ShutdownToken ) ) ) ;
10641059
10651060 foreach ( IEnumerable < PackageVulnerabilityMetadataContextInfo > vulnerabilityInfo in transitivePackageVulnerabilities )
10661061 {
@@ -1612,8 +1607,6 @@ private void CleanUp()
16121607
16131608 Model . Context . ProjectActionsExecuted -= OnProjectActionsExecuted ;
16141609
1615- Model . Context . SourceService . PackageSourcesChanged -= PackageSourcesChanged ;
1616-
16171610 Settings . SettingsChanged -= Settings_SettingsChanged ;
16181611
16191612 Model . Dispose ( ) ;
@@ -1806,6 +1799,7 @@ private void ExecuteSearchPackageCommand(object sender, ExecutedRoutedEventArgs
18061799
18071800 private async Task ExecuteRestartSearchCommandAsync ( )
18081801 {
1802+ await NuGetUIThreadHelper . JoinableTaskFactory . SwitchToMainThreadAsync ( ) ;
18091803 _packageVulnerabilityService ? . ResetVulnerabilityData ( ) ;
18101804 await SearchPackagesAndRefreshUpdateCountAsync ( useCacheForUpdates : false ) ;
18111805 await RefreshConsolidatablePackagesCountAsync ( ) ;
0 commit comments