From 34e5b381141a153972917ae675ffcd5568bd2c12 Mon Sep 17 00:00:00 2001 From: mfogliatto <2962955+mfogliatto@users.noreply.github.com> Date: Sun, 7 Jun 2026 08:05:40 +0000 Subject: [PATCH] fix: share ProjectCollection instance and implement IDisposable Refactor MSBuildProjectMetadataProvider to use a single ProjectCollection instance across both GetProjectReferences() and GetPropertyValue(), eliminating redundant project loads when both methods are called for the same project file. Changes: - MSBuildProjectMetadataProvider now holds a shared ProjectCollection created once in the constructor - Added LoadOrGetProject() helper that caches loaded projects via ProjectCollection.GetLoadedProjects(), avoiding double evaluation - Implemented IDisposable with proper dispose pattern to clean up the ProjectCollection (UnloadAllProjects + Dispose) - ReferenceCopTask.Execute() now disposes the provider in a finally block using safe cast (as IDisposable)?.Dispose() Fixes mfogliatto/ReferenceCop#68 --- .../MSBuildProjectMetadataProvider.cs | 58 +++++++++++++++++-- src/ReferenceCop.MSBuild/ReferenceCopTask.cs | 4 ++ 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/src/ReferenceCop.MSBuild/Providers/MSBuildProjectMetadataProvider.cs b/src/ReferenceCop.MSBuild/Providers/MSBuildProjectMetadataProvider.cs index 6710d5c..3f8ddaa 100644 --- a/src/ReferenceCop.MSBuild/Providers/MSBuildProjectMetadataProvider.cs +++ b/src/ReferenceCop.MSBuild/Providers/MSBuildProjectMetadataProvider.cs @@ -1,5 +1,6 @@ namespace ReferenceCop.MSBuild { + using System; using System.Collections.Generic; using System.Linq; using Microsoft.Build.Evaluation; @@ -7,11 +8,22 @@ namespace ReferenceCop.MSBuild /// /// Provides project reference information using MSBuild project evaluation. /// - public class MSBuildProjectMetadataProvider : IProjectMetadataProvider + public class MSBuildProjectMetadataProvider : IProjectMetadataProvider, IDisposable { private const string ProjectReferenceNode = "ProjectReference"; private const string NoWarnMetadata = "NoWarn"; + private readonly ProjectCollection projectCollection; + private bool disposed; + + /// + /// Initializes a new instance of the class. + /// + public MSBuildProjectMetadataProvider() + { + this.projectCollection = new ProjectCollection(); + } + /// /// Gets the project references from a project file. /// @@ -19,8 +31,7 @@ public class MSBuildProjectMetadataProvider : IProjectMetadataProvider /// The collection of project references. public IEnumerable GetProjectReferences(string projectFilePath) { - var projectCollection = new ProjectCollection(); - var project = projectCollection.LoadProject(projectFilePath); + var project = this.LoadOrGetProject(projectFilePath); // Get all ProjectReference items. These are the direct project references. var projectReferences = project.GetItems(ProjectReferenceNode); @@ -47,11 +58,48 @@ public IEnumerable GetProjectReferences(string projectFile /// The resolved property value. public string GetPropertyValue(string projectFilePath, string propertyName) { - var projectCollection = new ProjectCollection(); - var project = projectCollection.LoadProject(projectFilePath); + var project = this.LoadOrGetProject(projectFilePath); project.ReevaluateIfNecessary(); return project.GetPropertyValue(propertyName); } + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases the resources used by the . + /// + /// Whether managed resources should be disposed. + protected virtual void Dispose(bool disposing) + { + if (!this.disposed) + { + if (disposing) + { + this.projectCollection.UnloadAllProjects(); + this.projectCollection.Dispose(); + } + + this.disposed = true; + } + } + + private Project LoadOrGetProject(string projectFilePath) + { + var fullPath = System.IO.Path.GetFullPath(projectFilePath); + var loaded = this.projectCollection.GetLoadedProjects(fullPath); + + if (loaded.Count > 0) + { + return loaded.First(); + } + + return this.projectCollection.LoadProject(fullPath); + } } } diff --git a/src/ReferenceCop.MSBuild/ReferenceCopTask.cs b/src/ReferenceCop.MSBuild/ReferenceCopTask.cs index d370247..d94d9aa 100644 --- a/src/ReferenceCop.MSBuild/ReferenceCopTask.cs +++ b/src/ReferenceCop.MSBuild/ReferenceCopTask.cs @@ -120,6 +120,10 @@ public bool Execute() success = false; this.BuildEngine.LogErrorEvent(ex); } + finally + { + (this.projectReferencesProvider as IDisposable)?.Dispose(); + } return success; }