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;
}