Hugoware

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

Posts Tagged ‘Lambdas

Work Sequences Using Lambdas

with 2 comments

Occasionally I’ll run into some code that I want to “undo” in case the entire thing doesn’t go as planned. Normally placing this code into a separate class and then performing each work item one at a time works pretty well. You have to keep track of your progress so you know where to rollback from but overall it isn’t a very complicated process.

I had pretty much the same scenario the other day, but this time I thought I’d approach it a little differently. Instead of writing the sequence management into the class I was using I decided to see if I could come up with the same concept using lambdas instead.

[Download Source Code: WorkSequence.cs] : Hosted on gist.github.

The idea is to make some reusable code that can be used within a class to handle sequences of code. Let’s look at a sample of how you might use this code – We’re going to pretend this code is part of a class responsible for moving some data around. If one transfer fails then all of the work should be reverted.

string path = Path.Combine(@"\\server\share\", this.FileName);

//creating a default sequence
WorkSequence work = new WorkSequence();

//when a rollback takes place
work.Error += (sequence, item, index) => {
    string name = item.GetPerformMethod().Name;
    Console.WriteLine("Failed item {0}", name);
};

//when the work finished
work.Complete += (sequence) => {
    Console.WriteLine("Finished work!");
};

//1. calling a method without a rollback step
work.Add(this.GenerateLibrary);

//2. calling a method that has an undo step
work.Add(this.CreatePackage, this.RemoveTempCacheFile);

//3. calling methods that have arguments
work.Add(() => this.Transfer(path), () => File.Delete(path));

//4. calling a method using different credentials
work.Add(this.Archive, new ImpersonationContext("domain\\name", "passw0rd"));

//5. starts executing the work
work.Perform();

//6. or an async call
work.PerformAsync((sequence) => {
    Console.WriteLine("Finished in sequence");
});

The general idea for this code is to set the work that you need to do and then an optional ‘rollback’ option in case there is a problem. You’ll notice that none of the methods accept arguments. Instead, you can provide an action that invokes the action and then provides the arguments (in #3).

As each item is fired an index is used to keep track of the position in the sequence. That way, if there is an error, only the work that has actually executed will have the rollback method fired.

You’ll also notice that I’m using the code I recently published so you can provide credentials to use for impersonation. This can be per method or applied as a ‘default’ for the entire sequence.

Finally, you can either invoke the process and wait for all of the work to complete or fire it up in an asynchronously. You can provide a callback method or assign an event to fire off once the work has completed.

Not sure if there is a built in class to handle this kind of work but I’ve found it useful so far – Enjoy!

Advertisements

Written by hugoware

April 19, 2010 at 11:29 pm

Using Delegates As Parameters

with 2 comments

I’m not sure the community stance on passing around delegates as arguments but personally I love it. I think there is something magical about the way you can provide instructions on how how to do something and then pass it into another method and let it do its job without needing to think about it. It is almost like writing a code template and then filling in the blanks with the specifics of what you need.

Using delegates as arguments can simplify code and in some cases reduce it. This post goes over a sample scenario where you might use delegates as arguments instead of writing multiple functions to perform different tasks.

Simple Resource Loader

Let’s pretend we have a class that is responsible for loading and saving files from an external resource. We could write a class like the one below.


//handles loading resources for the application
public static class ResourceLoader {

    //returns the bytes for a resource
    public static byte[] LoadBytes(string resource) {
        string path = ResourceLoader._GetResourcePath(resource);
        return File.ReadAllBytes(path);
    }

    //returns a xml document resource
    public static XDocument LoadXmlDocument(string resource) {
        string path = ResourceLoader._GetResourcePath(resource);
        return XDocument.Load(path);
    }

    //returns a bitmap resource
    public static Bitmap LoadBitmap(string resource) {
        string path = ResourceLoader._GetResourcePath(resource);
        return Bitmap.FromFile(path) as Bitmap;
    }

    //returns the string text for a resource
    public static string LoadText(string resource) {
        string path = ResourceLoader._GetResourcePath(resource);
        return File.ReadAllText(path);
    }

    //generates a path to a resource
    private static string _GetResourcePath(string file) {
        return Path.Combine(@"c:\path\to\files\", file);
    }

}

Nothing is really wrong with this code. If we need to add a new type to this class then we simply create a new method and plug it in. Additionally, each of the methods could use some sort of exception handling in case the conversion doesn’t go so well. As you can imagine, the more try catch blocks we add, the larger this class starts to get.

However, if you think about it, all of these resources could be handled roughly the same way. They all need a way to convert bytes to whatever type you’re wanting.

Using A Delegate To Fill In The Blank

So instead, lets address this problem using a delegate to handle the conversion of bytes to the type that we need.


//handles loading resources for the application
public static class ResourceLoader {

    //returns the bytes for a resource
    public static T Load<T>(string resource, Func<byte[], T> convert) {

        //find the correct path
        string path = Path.Combine(@"c:\path\to\files\", resource);
        byte[] bytes = File.ReadAllBytes(path);

        //attempt convert the file
        try {
            return convert(bytes);
        }
        //if it fails, forward the error to the caller
        catch (Exception ex) {
            throw new FormatException(
                string.Format(
                    "Could not load resource '{0}' as a type {1}", 
                    resource, typeof(T).Name
                    ), ex);
        }

    }

}

Great — Now we can provide any method we want to format the bytes and then return the type we’re looking for. So for example, instead of calling ResourceHandler.LoadBitmap we can use our new method as shown below.


Bitmap bitmap = ResourceLoader.Load("test.jpg", (bytes) => {
    using (MemoryStream stream = new MemoryStream(bytes)) {
        return Bitmap.FromStream(stream) as Bitmap;
    }
});

Pretty slick, right? …oh, wait… this code is longer and more difficult to use… This can’t be right!

Bringing The Concept Together

Clearly, the example above isn’t improving anything for us. The code is longer and requires that we write the same functionality for reading a resource in multiple places. Even though this code is more flexible, it also requires more work. So instead, lets write some definitions of common conversions as part of our class.


//handles loading resources for the application
public static class ResourceLoader {

    //Load<T>(resource, convert)
    //snip...

    //converts bytes to a bitmap
    public static readonly Func<byte[], Bitmap> AsBitmap = (bytes) => {
        using (MemoryStream stream = new MemoryStream(bytes)) {
            return Bitmap.FromStream(stream) as Bitmap;
        }
    };

    //converts bytes to a xml document
    public static readonly Func<byte[], XDocument> AsXml = (bytes) => {
        using (MemoryStream stream = new MemoryStream(bytes)) {
            using (StreamReader reader = new StreamReader(stream)) {
                return XDocument.Parse(reader.ReadToEnd());
            }
        }
    };

    //converts bytes to a string
    public static readonly Func<byte[], string> AsText = (bytes) => {
        return Encoding.UTF8.GetString(bytes);
    };

    //simply returns the byte array
    public static readonly Func<byte[], byte[]> AsBytes = (bytes) => bytes;

}

Now, instead of needing to manually create a delegate to handle the conversion process we can write code like the example below.

 
//Loads a resource by passing the static delegate that is part of the class we created
Bitmap bitmap = ResourceLoader.Load("test.jpg", ResourceLoader.AsBitmap);
 

By using delegates as an argument we allow our loading function to be flexible enough to accept other methods for performing the conversion (that we can create as we need them) or the common conversion methods that we added as part of the class.

You’ll also notice that we don’t need to declare the generic type we’re wanting to have returned since it can be inferred from the return type of the delegate we pass in! (so cool… to me at least)

Of course, there is about a hundred different ways to can already load resources in .NET but you could apply this same concept many other areas of applications you develop.

How could you use delegates to create “templates” for code?

Written by hugoware

January 13, 2010 at 1:58 am

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