Wednesday, May 27, 2009

Yield Passes Control Through Intermediate Calls

I was wanting to be able to perform a backend database service in a batch mode, but wanted to make the actual batch processing part of it as encapsulated as possible.

I created a BO that returned an IEnumerable<List<MyBO>>, but it didn't use any yield statements, it just called a data access layer that was using the yield statements. I was concerned if this was going to work, so I decided to test it using snippet compiler.

So this is my quick and dirty test to see if it would work:

public static void RunSnippet()
{
foreach (List<string> answers in GetAnswers())
{
WL(answers.Count);
foreach (string answer in answers)
{
WL(answer);
}
}
}

public static IEnumerable<List<string>> GetAnswers()
{
return GetTheAnswers();
}

private static IEnumerable<List<string>> GetTheAnswers()
{
List<string> answers = new List<string>();
answers.Add("A");
answers.Add("B");
answers.Add("C");
answers.Add("D");
yield return answers;

answers = new List<string>();
answers.Add("D");
answers.Add("C");
answers.Add("B");
answers.Add("A");
yield return answers;

answers = new List<string>();
answers.Add("X");
answers.Add("Y");
answers.Add("Z");
yield return answers;
}






And these were the results:
4
A
B
C
D
4
D
C
B
A
3
X
Y
Z


So even though GetAnswers() gets called once, GetTheAnswers() will return control back to the enumerating foreach multiple times.

Wednesday, May 13, 2009

Searching Through All Controls in a ControlCollection

I had a user control at work that dynamically added TextBoxes to a HtmlTable.  After figuring out how to even access the dynamic controls, I needed to be able to retrieve them easily.  Since the ids were based on a primary key id from the database, I couldn't determine what the ids of the TextBoxes were using the FindControl() method.  I could hit the database again to find the ids and then use the FindControl method, but I didn't like having another database hit if I could help it.

What I wanted to be able to do was enumerate over all of the controls located within my user control.  After thinking about it for a bit, I decided to finally make use of the C# "yield" statement and create my own recursive iterator method:

public static IEnumerable<Control> GetAllControls(this ControlCollection controls)
{
foreach (Control control in controls)
{
yield return control;

foreach(Control childControl in control.Controls.GetAllControls()){
yield return childControl;
}
}
}

Now I could do a foreach against all controls within a ControlCollection, and process any TextBoxes:

foreach (Control control in PlaceHolder1.Controls.GetAllControls())
{
if (control.GetType() == typeof(TextBox))
{
// Do Work Here
}
}

I believe that this could be extremely useful and wonder why it wasn't included in .Net to begin with.

ASP.Net Control to Display Session

I wanted to be able to see all my Session data on my webpage, but only when I actually had a debugger attached.  My solution was a two step approach:
  1. Create a user control that ouputs the session into a table
  2. Add that user control to the to the page if a debugger is attached
To accomplish step 1, I created a user control which is located below.  It overrides the Render method, directly writing a text html table to the HtmlTextWriter.  It contains two other methods, one to create the table to display the session information in, iterating over all the values in the session, and another that uses reflection to output the value of all properties of non-simple types.  

public class SessionDisplayControl : UserControl

public SessionDisplayControl()
{
}

protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
writer.Write(GetOutputState());
}

protected string GetOutputState()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder(2000);
sb.AppendLine("<table border='1' align='center'><tr><td colspan='2'>There are " + Session.Contents.Count + " Session variables</td></tr>");
foreach (string name in Session.Contents)
{
System.Collections.IEnumerable enumeratable = Session[name] as System.Collections.IEnumerable;
if (enumeratable == null || Session[name].GetType() == typeof(String))
{
sb.AppendLine(string.Format(@"<tr><td>{0}</td><td>{1}</td></tr>", HttpUtility.HtmlEncode(name), GetObjectValue(Session[name] ?? "<NULL>")));
}
else
{
sb.AppendLine(@"<tr><td>" + HttpUtility.HtmlEncode(name) + @"</td><td><table border='1'>");
int i = 0;
foreach (object o in enumeratable)
{
sb.AppendLine(string.Format("<tr><td>Item({0})</td>", i++));
sb.AppendLine(string.Format(@"<td>{0}</td></tr>", GetObjectValue(o)));
}
sb.AppendLine(@"</table></tr>");
}
}
sb.AppendLine("</table>");
return sb.ToString();
}

private static string GetObjectValue(Object obj)
{
if (obj is string || obj is bool || obj is int || obj is long || obj is double || obj is decimal || obj is DateTime)
{
return HttpUtility.HtmlEncode(obj.ToString());
}

System.Text.StringBuilder sb = new System.Text.StringBuilder(500);
System.Reflection.PropertyInfo[] properties = obj.GetType().GetProperties();
foreach (System.Reflection.PropertyInfo property in properties)
{
try
{
sb.Append(HttpUtility.HtmlEncode(property.Name));
sb.Append(": ");
sb.Append(HttpUtility.HtmlEncode((property.GetValue(obj, null) ?? "<NULL>").ToString()));
sb.Append("<br/>");
}
catch (Exception ex)
{
sb.Append("ERROR: " + HttpUtility.HtmlEncode(ex.Message) + "<br/>");
}
}
return sb.ToString();
}
}

To accomplish step 2, all that was needed was to override the OnLoad event in my BasePage class from which all other classes in my website inherit, check for a debugger, and add my user control from step 1 to the page if one was attached.

    protected override void  OnLoad(EventArgs e)
{
base.OnLoad(e);
if (System.Diagnostics.Debugger.IsAttached && !IsPostBack)
{
this.Controls.Add(new SessionDisplayControl());
}
}

Now whenever I have a debugger attached my session will be displayed at the bottom of the page.  This has helped me clean up unused session variables numerous times.  Enjoy!

Accessing Controls Dynamically Created During AJAX PartialPostBacks

I had an extremely annoying issue trying to be able to create textboxes dynamically within an AJAX UpdatePanel during on the PostBack.  Below is the code that I used to create a textbox, and added it to a TableCell.  It runs during an AJAX PartialPostback created on a DropDownList SelectedIndexChangedEvent.

TextBox answerText = new TextBox();
answerText.ID = "AnswerTextBox" + question.ServiceRequestQuestionId;
answerText.Attributes.Add("questionid", question.ServiceRequestQuestionId.ToString());
answerText.TextMode = TextBoxMode.MultiLine;
answerText.Style.Add(HtmlTextWriterStyle.Width, "100%");
answerText.Columns = 4;
answerText.Rows = 4;
answerText.Attributes.Add("runat", "server");
answerCell.Controls.Add(answerText);



My TextBox was showing up exactly how I wanted it to, but when I'd attempt to access the textbox after the user clicked the button submit, the textbox didn't exist anywhere on my page.  After googling for a bit, it made sense to me that I had to re-add my dynamic control to my page, but what I couldn't find was how to access the answerText.Text value from the ViewState.  I finally discovered that the TextBox had to be readded during the Page_Load or Page_Init events in order to be able to access their ViewState values, and it is only after the Page_Load event finishes that the values actually become accessible, and only if you add the objects in the some location within the heirarchy, and with the same id value.

I was thinking that somehow automagically I should be able to access this controls from the ViewState, and when I readded the control, it would override the value.  Boy was I wrong.