Dealing with “System.InvalidOperationException: Collection was modified; enumeration operation may not execute” error message in provisioning code.

This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at http://www.microsoft.com/info/cpyright.htm

 

This error message always gets me by surprise, so finally I decided to document this potential pitfall.

I typically get this error message in the provisioning code when I am trying to disconnect multiple connectors from different connector spaces. So here a sample code that will cause this error

 

If mventry(“oracleEnabled”).value = false

 For Each MA As ManagementAgent In mventry.ConnectedMAs

    If MA.Name.StartsWith("oracle") Then

      Mventry.ConnectedMAs(MA.Name).Connectors.DeprovisionAll()

    End If

 Next

Endif

 

So in this peace of code I am checking if the oracleEnabled Boolean attribute is set to false, and if yes (according to my business logic), I need to go through all of the connectors for Oracle and disconnect them. At first, this peace of code looks quite logical, we go through every connector space attached to the mventry and if it is an Oracle connector space, we deprovision all connectors. But if this code gets executed you will get an exception: System.InvalidOperationException: Collection was modified; enumeration operation may not execute.

So what is the problem with this code? Well, the issue is really not related to MIIS, but rather how .NET handles enumeration of collections. Basically, the issue here is that inside the FOR EACH loop we are changing the collection that is used to control the loop. So in our case, we are using mventry.ConnectedMAs collection to control the loop, but inside the loop we are disconnecting some of the connector spaces which changes the mventry.ConnectedMAs collection, which in turn impacts the enumeration of the loop, and .NET throws an exception and rightly so.

So how do we get around this? Here is one way, simply save the MA names to a string array and then use that array for controlling the FOR EACH loop.

 

Dim ConnectedOraMAs() As String

Dim i As Integer = 0

    For Each MA As ManagementAgent In mventry.ConnectedMAs

        If MA.Name.StartsWith("oracle") Then

           ReDim Preserve ConnectedOraMAs(i)

           ConnectedOraMAs(i) = MA.Name

           i = i + 1

         End If

Next

If Not ConnectedOraMAs Is Nothing Then

If mventry(“oracleEnabled”).value = false Then

For Each MA As String In ConnectedOraMAs

mventry.ConnectedMAs(MA).Connectors.DeprovisionAll()

Next

End If