Description
MSBuildProjectMetadataProvider creates a new ProjectCollection() and calls LoadProject() in both GetProjectReferences() and GetPropertyValue(). In ReferenceCopTask.Execute(), both methods are called for the same project file, meaning the project is loaded and parsed twice per task invocation.
Additionally, ProjectCollection implements IDisposable but is never disposed, leaking unmanaged resources.
Affected Files
src/ReferenceCop.MSBuild/Providers/MSBuildProjectMetadataProvider.cs — lines 22-23 and 50-51
src/ReferenceCop.MSBuild/ReferenceCopTask.cs — Execute() calls both methods sequentially
Impact
- Double project load:
ProjectCollection.LoadProject() involves full MSBuild project evaluation (parsing XML, evaluating properties, resolving imports). Doing this twice for the same file is wasteful.
- Resource leak:
ProjectCollection is IDisposable and holds unmanaged resources (COM interop, file handles). Never disposing it causes resource leaks, especially across a multi-project build where the MSBuild task runs per-project.
- Build-time cost: This compounds across a solution — for N projects, there are 2N unnecessary project evaluations.
Suggested Optimization
Share a single ProjectCollection instance per method call, or refactor to load the project once and extract both references and properties:
public class MSBuildProjectMetadataProvider : IProjectMetadataProvider, IDisposable
{
private readonly ProjectCollection projectCollection = new ProjectCollection();
public IEnumerable<ProjectReferenceInfo> GetProjectReferences(string projectFilePath)
{
var project = this.projectCollection.LoadProject(projectFilePath);
// ... extract references ...
}
public string GetPropertyValue(string projectFilePath, string propertyName)
{
// Reuse already-loaded project from the collection
var project = this.projectCollection.GetLoadedProjects(projectFilePath).FirstOrDefault()
?? this.projectCollection.LoadProject(projectFilePath);
project.ReevaluateIfNecessary();
return project.GetPropertyValue(propertyName);
}
public void Dispose()
{
this.projectCollection.Dispose();
}
}
Alternatively, a simpler approach: load the project once in ReferenceCopTask.Execute() and pass the loaded Project object to both operations.
Description
MSBuildProjectMetadataProvidercreates a newProjectCollection()and callsLoadProject()in bothGetProjectReferences()andGetPropertyValue(). InReferenceCopTask.Execute(), both methods are called for the same project file, meaning the project is loaded and parsed twice per task invocation.Additionally,
ProjectCollectionimplementsIDisposablebut is never disposed, leaking unmanaged resources.Affected Files
src/ReferenceCop.MSBuild/Providers/MSBuildProjectMetadataProvider.cs— lines 22-23 and 50-51src/ReferenceCop.MSBuild/ReferenceCopTask.cs—Execute()calls both methods sequentiallyImpact
ProjectCollection.LoadProject()involves full MSBuild project evaluation (parsing XML, evaluating properties, resolving imports). Doing this twice for the same file is wasteful.ProjectCollectionisIDisposableand holds unmanaged resources (COM interop, file handles). Never disposing it causes resource leaks, especially across a multi-project build where the MSBuild task runs per-project.Suggested Optimization
Share a single
ProjectCollectioninstance per method call, or refactor to load the project once and extract both references and properties:Alternatively, a simpler approach: load the project once in
ReferenceCopTask.Execute()and pass the loadedProjectobject to both operations.