Our product, Secret Server, now supports ASP.NET
2.0. Testing on ASP.NET 2.0 started with a horrible crash on the
secret view page resulting in the typical "but it worked fine in
1.1?!".
Here is the exception stack trace, we were
seeing:
Message: Collection was modified; enumeration
operation may not execute.
Exception:
System.InvalidOperationException
StackTrace:
at
System.Web.UI.ControlCollection.ControlCollectionEnumerator.MoveNext()
at
System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection
children)
at
System.Web.UI.Control.RenderChildren(HtmlTextWriter
writer)
at
System.Web.UI.WebControls.WebControl.RenderContents(HtmlTextWriter
writer)
at
System.Web.UI.WebControls.WebControl.Render(HtmlTextWriter
writer)
at
System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer,
ControlAdapter adapter)
at
System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter
adapter)
at
System.Web.UI.Control.RenderControl(HtmlTextWriter
writer)
at
System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection
children)
at
System.Web.UI.Control.RenderChildren(HtmlTextWriter
writer)
at
System.Web.UI.HtmlControls.HtmlForm.RenderChildren(HtmlTextWriter
writer)
at
System.Web.UI.HtmlControls.HtmlForm.Render(HtmlTextWriter
output)
at
System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer,
ControlAdapter adapter)
at
System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter
adapter)
at
System.Web.UI.HtmlControls.HtmlForm.RenderControl(HtmlTextWriter
writer)
at
System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection
children)
at
System.Web.UI.Control.RenderChildren(HtmlTextWriter
writer)
at
System.Web.UI.HtmlControls.HtmlContainerControl.Render(HtmlTextWriter
writer)
at
System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer,
ControlAdapter adapter)
at
System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter
adapter)
at
System.Web.UI.Control.RenderControl(HtmlTextWriter
writer)
at
System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection
children)
at
System.Web.UI.Control.RenderChildren(HtmlTextWriter
writer)
at System.Web.UI.Page.Render(HtmlTextWriter
writer)
at
Thycotic.Foundation.WebControls.BasePage.RenderMiddle(HtmlTextWriter
writer)
at
Thycotic.Foundation.WebControls.BasePage.RenderAll(HtmlTextWriter
writer)
at
Thycotic.Foundation.WebControls.BasePage.Render(HtmlTextWriter
writer)
at
System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer,
ControlAdapter adapter)
at
System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter
adapter)
at
System.Web.UI.Control.RenderControl(HtmlTextWriter
writer
at System.Web.UI.Page.ProcessRequestMain(Boolean
includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
Putting on our deerstalker and tapping our pipe,
this stack trace still doesn't reveal the culprit and is not particularly
helpful at all. The exception message sounds like the error you get
when modifying the collection inside a foreach loop, right? But
where?! - it appears to be happening in the Render method of one of
the controls on the page - but which one?! The error was finally trapped
down through a slow process of trial and error to a custom webcontrol (in a
compiled referenced dll, no less!) that has a curious ReadOnly capability
that works fine in 1.1 but dies miserably in 2.0.
Here is the code:
1 protected override void Render(HtmlTextWriter writer)
2 {
3 if (_readOnly)
4 {
5 Label label = new Label();
6 label.ID = this.ID;
7 label.Text = this.Text;
8 ReplaceSelfWithControl(label);
9 label.RenderControl(writer);
10 }
11 else
12 {
13 base.Render(writer);
14 }
15 }
16
17 private void ReplaceSelfWithControl(Control control)
18 {
19 this.Parent.Controls.AddAt(this.Parent.Controls.IndexOf(this), control);
20 this.Parent.Controls.Remove(this);
21 }
The problem (as you probably have guessed) is the
ReplaceSelfWithControl method where the TextBox reaches back into the Controls
collection and replaces itself with a Label. Changing the code to simply render
the HTML output fixed the problem, like so:
1 protected override void Render(HtmlTextWriter writer)
2 {
3 if (_readOnly)
4 {
5 if (this.CssClass != null && this.CssClass.Trim() != "")
6 {
7 writer.AddAttribute("class", this.CssClass);
8 }
9 writer.AddAttribute("id", this.ClientID);
10 writer.RenderBeginTag(HtmlTextWriterTag.Span);
11 writer.Write(this.Text);
12 writer.RenderEndTag();
13 }
14 else
15 {
16 base.Render(writer);
17 }
18 }
Hopefully this will help someone else tackling a
similar problem.
Jonathan Cogley is the CEO and founder of
thycotic, a .NET consulting company and ISV in Washington DC. thycotic has
just released Thycotic Secret Server which is a secure web-based solution to both "Where is my Hotmail
password?" and "Who has the password for our domain name?". Secret Server
is the leader in secret management and sharing within companies and
teams.