0

I'm running some unit tests in parallel. Each unit test invokes some code that triggers an MSBuild deployment of an SSDT database project:

void Deploy(string projectPath, string publishProfilePath, ILogger logger)
{
    var project = ProjectCollection.GlobalProjectCollection.LoadProject("projectPath);

    Projects.SetGlobalProperty("Configuration", "Debug");
    Projects.SetGlobalProperty("SqlPublishProfilePath", publishProfilePath);
    var targets = new[] { "Build", "Publish" };

    project.Build(targets, new[] { logger });
}

The last line of this method throws an InvalidOperationException with the following message:

This collection is read-only.

Here's the relevant part of the call stack trace:

   at Microsoft.Build.Shared.ErrorUtilities.ThrowInvalidOperation(String resourceName, Object[] args)
   at Microsoft.Build.Collections.RetrievableEntryHashSet`1.AddEvenIfPresent(T value)
   at Microsoft.Build.Evaluation.Project.Data.AddTarget(ProjectTargetInstance target)
   at Microsoft.Build.Evaluation.Evaluator`4.ReadTargetElement(ProjectTargetElement targetElement, LinkedList`1 activeTargetsByEvaluationOrder, Dictionary`2 activeTargets)
   at Microsoft.Build.Evaluation.Evaluator`4.Evaluate()
   at Microsoft.Build.Evaluation.Evaluator`4.Evaluate(IEvaluatorData`4 data, ProjectRootElement root, ProjectLoadSettings loadSettings, Int32 maxNodeCount, PropertyDictionary`1 environmentProperties, ILoggingService loggingService, IItemFactory`2 itemFactory, IToolsetProvider toolsetProvider, ProjectRootElementCache projectRootElementCache, BuildEventContext buildEventContext, ProjectInstance projectInstanceIfAnyForDebuggerOnly)
   at Microsoft.Build.Evaluation.Project.ReevaluateIfNecessary(ILoggingService loggingServiceForEvaluation)
   at Microsoft.Build.Evaluation.Project.CreateProjectInstance(ILoggingService loggingServiceForEvaluation, ProjectInstanceSettings settings)
   at Microsoft.Build.Evaluation.Project.Build(String[] targets, IEnumerable`1 loggers, IEnumerable`1 remoteLoggers)
   at Microsoft.Build.Evaluation.Project.Build(String[] targets, IEnumerable`1 loggers)

The documentation for the Build method doesn't seem to mention this. This code has worked before.

Sam
  • 40,644
  • 36
  • 176
  • 219

1 Answers1

0

I suspect that MSBuild is not thread-safe in this manner.

The problem no longer occurred when I made my code thread-safe by prevented multiple simultaneous calls to it using lock:

void Deploy(string projectPath, string publishProfilePath, ILogger logger)
{
    lock (deployLock)
    {
        //Implementation
    }
}

object deployLock = new object();
Sam
  • 40,644
  • 36
  • 176
  • 219