Hugoware

The product of a web developer with a little too much caffeine

Posts Tagged ‘Samples

Quirks Of Using For Each

leave a comment »

Collections can be tricky sometimes when you’re writing code. This is especially true when you need to modify them while you’re looping through them. Let’s look at a couple examples that can get you into trouble.

//create a typical list of items
List<string> items = new List<string> {
    "first", "second", "third", "fourth"
};

//now clear the list
foreach (string item in items) {
    items.Remove(item);
}

Of course, this won’t work. Why?

InvalidOperationException: Collection was modified; enumeration operation may not execute

Once our loop has started the boundaries of the collection have been defined. Adding or removing items will change those boundaries and create errors. Naturally, most languages aren’t going to like this and will crash.

Fortunately, we do get an Exception message (which is the best kind of error message). However, we should be more concerned about subtle problems that could arise if we change a collection while looping through it. For example, consider the ForEach method attached to Generic Lists…

//get a list of the employees that 
//should be deleted from the system
List<string> firedEmployees = new List<string> {
    "jeff", "blake", "steve"
};

//and delete them
firedEmployees.ForEach(employee => {
    EmployeeSystem.Delete(employee);
    firedEmployees.Remove(employee);
});

Everyone deleted? Nope…

I don’t know about you, but I was surprised to find that one employee remained on this list after the code was run (the second employee at that). ForEach made sure to keep track of our collection as it changed but unfortunately it created an undesirable result.

To take it a step further, if you were to add a new item each time then you’d end up with an infinite loop, an error that would be caught sooner but a problem all the same.

This straight forward code has potential for some serious problems.

So, abandon the framework! Move to Java! .NET is doomed!

…Or we can just write an extension method to take care of both problems at the same time.

public static class EnumerableExtensions {

    /// <summary>
    /// Safely enumerates through a collection
    /// </summary>
    public static IEnumerable<T> Each<T>(this IEnumerable<T> collection, Action<T> action) {

        //move the collection into its own list and
        //perform the work on each item
        collection.ToList().ForEach(action);
        return collection;

    }

}

The fix itself isn’t very impressive. One additional call to ToList() and we’re pretty much okay. But this does keep the original collection safe while we enumerate through the protected one.

Advertisements

Written by hugoware

July 22, 2010 at 11:03 pm

Undo, Redo and Whatever… From A Web Application

with 2 comments

Long ago a brilliant developer invented the Undo command — probably one of the most important features to be added in computing history. It seems funny to me that today, in 2009, it is still a very rare feature to find on a website. This isn’t to say no one is doing it but more that it isn’t a common feature to find.

Using Lambdas For Undo Actions

In .NET you can use Lambdas to create a function and pass it around like an argument and then that argument can then be invoked at a later time in a different place. That said, with a little careful planning and a few correctly stored Lambdas, you could have a working ‘Undo’ model in your ASP.NET web sites For this example I’m going to use MVC but this would really work anywhere.

Warning: This is very crude code, more of a proof of concept, so you probably don’t want to use it in your projects as is.

Let’s start with an abstract controller that contains our Undo handling code.

[UndoController.cs]

using System;
using System.Web;
using System.Web.Mvc;

namespace UndoAction {

	//class that supports an undo action
	public class UndoController : Controller {

		#region Constants

		private const string SESSION_UNDO_ACTION = "Session:UndoAction";
		private const string SESSION_UNDO_MESSAGE = "Session:UndoMessage";
		private const string TEMP_DATA_UNDO_MESSAGE = "UndoResult";
		private const string DEFAULT_UNDO_MESSAGE = "Previous action was undone!";
		private const string DEFAULT_MISSING_UNDO_MESSAGE = "No actions to undo!";

		#endregion

		#region Undo Action Container (Session)

		//contains the current undo action
		private static Action _UndoAction {
			get {
				return System.Web.HttpContext.Current
					.Session[SESSION_UNDO_ACTION] as Action ;
			}
			set {
				System.Web.HttpContext.Current
					.Session[SESSION_UNDO_ACTION] = value;
			}
		}

		//the message to return in the view data for this action
		private static string _UndoMessage {
			get {
				return System.Web.HttpContext.Current
					.Session[SESSION_UNDO_MESSAGE] as string ;
			}
			set {
				System.Web.HttpContext.Current
					.Session[SESSION_UNDO_MESSAGE] = value;
			}
		}

		#endregion

		#region Setting Undo Actions

		/// <summary>
		/// Applies an undo action
		/// </summary>
		protected void SetUndo(Action action) {
			this.SetUndo(DEFAULT_UNDO_MESSAGE, action);
		}

		/// <summary>
		/// Applies an undo action with a return message for the user
		/// </summary>
		protected void SetUndo(string message, Action action) {
			UndoController._UndoAction = action;
			UndoController._UndoMessage = message;
		}

		/// <summary>
		/// Performs the undo action (if any) and saves the message
		/// </summary>
		protected void PerformUndo() {

			//check if there is an action
			if (UndoController._UndoAction is Action) {

				//perform the action and save the message
				Action action = UndoController._UndoAction;
				action();
				this.TempData[TEMP_DATA_UNDO_MESSAGE] =
					UndoController._UndoMessage;

				//and clear out the previous information
				UndoController._UndoAction = null;
				UndoController._UndoMessage = null;

			}
			//just save a generic message
			else {
				this.TempData[TEMP_DATA_UNDO_MESSAGE] =
					DEFAULT_MISSING_UNDO_MESSAGE;
			}

		}

		#endregion

	}

}

Basically, we create an abstract Controller that allows us to set an Undo action using a Lambda. You can also set a message for to return to the user that sumarizes what the action had done. This example uses a couple Session variables to hold the parts of the undo action, except I recommend that you create an actual class to house all of the information.

Next, let’s look at how we would actually use this controller.

[HomeController.cs]

using System;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Linq;

namespace UndoAction {

	//example of using undo actions
	public class HomeController : UndoController {

		#region Constants

		private const string SESSION_LIST_CONTAINER = "Session:ListContainer";

		#endregion

		#region Properties

		//a simple list of items for the list
		public static List<string> ListContainer {
			get {
				List<string> container = System.Web.HttpContext.Current.Session[SESSION_LIST_CONTAINER] as List<string>;
				if (container == null) {
					container = new List<string>();
					System.Web.HttpContext.Current.Session[SESSION_LIST_CONTAINER] = container;
				}
				return container;
			}
		}

		#endregion

		#region Actions

		//shows the list of items
		public ActionResult Index() {
			return this.View(HomeController.ListContainer.OrderBy(item =&gt; item.ToLower()));
		}

		//adds an item to the list
		public ActionResult Add(string phrase) {

			//format the value
			phrase = (phrase ?? string.Empty).Trim();

			//add the item if it isn't there yet
			if (!HomeController.ListContainer.Any(item => item.Equals(phrase, StringComparison.OrdinalIgnoreCase)) &amp;&amp;
			    !string.IsNullOrEmpty(phrase)) {
				HomeController.ListContainer.Add(phrase);
			}

			//return to the list view
			return this.RedirectToAction("Index");

		}

		//removes an item from the list
		public ActionResult Delete(string phrase) {

			//make sure the item even exists first
			if (HomeController.ListContainer.Any(item => item.Equals(phrase, StringComparison.OrdinalIgnoreCase))) {

				//since it exists, save the logging message
				this.SetUndo(
					string.Format("Restored '{0}' to the list!", phrase),
					() => {
						HomeController.ListContainer.Add(phrase);
					});

				//and then actually remove it
				HomeController.ListContainer.Remove(phrase);

			}

			//return to the main page
			return this.RedirectToAction("Index");

		}

		//tries to undo the previous action
		public ActionResult Undo() {

			//attempts to undo the previous action (if any)
			this.PerformUndo();

			//return to the main page
			return this.RedirectToAction("Index");
		}

		#endregion

	}

}

This controller allows the user to manage a simple shopping list with an Undo action to allow them to restore a deleted item. It is some ugly code, but you get the idea on how it works.

The important thing you should note is that the Lambda doesn’t refer to the instance of the class (this) but instead that the list is a static property. This is important to keep in mind as you develop something like this. When you create the Lambda action, you can’t refer to the instance that it was created in. Instead, you need to work with static properties.

Finally, let’s look at the ViewPage that is used in this example.

[Index.aspx]

<%@ Page Language="C#" 
	Inherits="System.Web.Mvc.ViewPage" %>
<%@ Import Namespace="System.Collections.Generic" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<title>Shopping List</title>
	</head>
	<body>
		<% IEnumerable<string> list = this.Model as IEnumerable<string>; %>
		
		<h2>Shopping List</h2>
		
		<p><% =this.Html.ActionLink("Undo Previous Action", "Undo") %></p>
		
		<% if (this.TempData["UndoResult"] is string) { %>
		<p><em><% =this.TempData["UndoResult"] as string %></em></p>
		<% } %>
		
		<hr />
		
		<table>
		<% foreach(string item in list) { %>
			<tr>
				<td><% =this.Html.Encode(item) %></td>
				<td><% =this.Html.ActionLink("Remove", "Delete", new { phrase = item }) %></td>
			</tr>
		<% } %>
		</table>
		
		<hr />
		
		<form action="<% =this.Url.Action("Add") %>" method="post" >
			<strong>Add an item:</strong>
			<input type="text" name="phrase" />
			<input type="submit" value="Add" />
		</form>
		
	</body>
</html>

The ViewPage itself is nothing fancy — If you’re working in a project you should create a strongly-typed view but in this example we just read the information we need and go on.

Using The Page

Below are a few screen shots of what happens as the user makes changes to the page. This example starts with a list of a few different items.

After removing an item from the list (the ‘Orange’) pressing the ‘Undo Previous Action’ button will restore the item. The custom message that was added is displayed (from the TempData field).

If you press the ‘Undo’ button again there aren’t any actions waiting anymore and the default message is displayed instead (no actions waiting).

Things To Consider

This code is just a sample of how you could implement and Undo function into your web applications. Here are a few things that you might want to keep in mind before starting something like this.

  • Make sure that your Undo command doesn’t cause you to duplicate code. In this example, the undo command uses a different ‘Add’ approach than the actual ‘Add’ action on the controller. This could result in duplicated code or incorrect results.
  • Determine if you can queue up ‘Undo’ commands. This example can only do one action at a time, but you could potentially create a Stack of actions that are executed in the reverse order that they were added.
  • Could you use the same concept to ‘Redo’ a command after it has been undone?
  • Be careful with your context – Don’t use ‘this’ inside of your Undo Lambdas.

Anyways, just an interesting concept to share. This approach requires a lot more thought but could pay off in the end.

Do you have a web application that could benefit from an ‘Undo’ command?

Written by hugoware

December 21, 2009 at 12:03 am