Attention: We are retiring the ASP.NET Community Blogs. Learn more >

Secret Server now supports ASP.NET 2.0!

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.

No Comments