Archive

Archive for May, 2008

Making session variables a little easier to work with

May 29, 2008 4 comments

It’s been quiet on here for a while – that’s because I’ve been warped back to WebForms land where everything is craziness and it takes you half a day just to figure out wth is going on because you have to work with logic spread across a half dozen code behind files that all run to a couple thousand lines (no exaggeration!).

The app I am currently working on uses session variables a lot, and doesn’t clean them up very often, leading to odd behaviors due to stale state (among other things).

The first thing I do when I come across a page using session variables is extract them all into properties at the top of the page – this ensures those ugly string indexers are only in 2 places (the get and set) and you only have to write the cast once.  The problem I find with this is that to figure out if the session variable has a value or clean up the session variable I still have to address it using the string indexer, sure I can wrap that in a simple method call so I only have the indexer in 3 or 4 places, but when you have a page with 6 session variables, this starts to add a lot of cruft to the code behind.  What I really wanted was a simple way of adding a few methods to the property so I could do something like this…

int newValue = 0;
if( MySessionVar.HasValue )
{
  newValue += MySessionVar;
}
MySessionVar.Clear();

With a little bit of poking around, some generics and a simple implicit cast, I ended up with this:

using System;
using System.Web;

namespace Blog.Code
{
    ///<summary>
    /// Wraps a number of convenience methods around session variables allowing for a more fluent
    /// expression of the intent.
    ///</summary>
    ///<typeparam name="T">The type of the value to store in the session.</typeparam>
    public class SessionVariable<T> : IEquatable<SessionVariable<T>>
    {
        private readonly T _tempValueStore;
        private readonly string _sessionKey;

        ///<summary>
        /// The stored value or the default value for the type
        /// if there is no value associated with the sessionkey
        ///</summary>
        public T Value
        {
            get
            {
                object value = HttpContext.Current.Session[_sessionKey];
                if(value == null)
                {
                    return default( T );
                }

                return (T)value;
            }
        }

        ///<summary>
        /// Indicates if there is a value stored.
        ///</summary>
        public bool HasValue
        {
            get
            {
                return HttpContext.Current.Session[ _sessionKey ] != null;
            }
        }

        ///<summary>
        /// Creates a new SessionVariable to access the value stored in <paramref name="sessionKey"/>
        ///</summary>
        ///<param name="sessionKey">The key used to access the value in the session</param>
        public SessionVariable( string sessionKey )
        {
            _sessionKey = sessionKey;
        }

        ///<summary>
        /// Creates a new SessionVariable to store the value provided.
        ///</summary>
        ///<param name="value">The value to store.</param>
        /// <remarks>
        /// Value is not stored until <see cref="Store"/> is called.
        /// </remarks>
        public SessionVariable( T value )
        {
            _tempValueStore = value;
        }

        ///<summary>
        /// Stores the value in the session using the key specified.
        ///</summary>
        ///<param name="sessionKey">The key used to store the value.</param>
        public void Store(string sessionKey)
        {
            HttpContext.Current.Session[ sessionKey ] = _tempValueStore;
        }

        ///<summary>
        /// Removes the entry from the session.
        ///</summary>
        public void Clear()
        {
            HttpContext.Current.Session.Remove( _sessionKey );
        }

        ///<summary>
        /// Implicitly converts the specified value into a SessionVariable instance.
        ///</summary>
        ///<param name="value">The value to convert.</param>
        ///<returns>A SessionVariable instance holding the specified value.</returns>
        public static implicit operator SessionVariable<T>(T value)
        {
            return new SessionVariable<T>( value );
        }

        ///<summary>
        /// Implicitly converts the SessionVariable back to the type of the wrapped value.
        ///</summary>
        ///<param name="sessionVariable">The SessionVariable to convert</param>
        ///<returns>The wrapped value.</returns>
        public static implicit operator T(SessionVariable<T> sessionVariable)
        {
            return sessionVariable.Value;
        }

        public override string ToString()
        {
            return Value.ToString();
        }

        public bool Equals( SessionVariable<T> sessionVariable )
        {
            if ( sessionVariable == null )
            {
                return false;
            }
            return Equals( Value, sessionVariable.Value );
        }

        public override bool Equals( object obj )
        {
            if ( ReferenceEquals( this, obj ) )
            {
                return true;
            }
            return Equals( obj as SessionVariable<T> );
        }

        public override int GetHashCode()
        {
            return Value.GetHashCode();
        }
    }
}

Which can be used like this:

protected SessionVariable<string> SuccessMessage
{
    get
    {
        return new SessionVariable<string>("SuccessMessage");
    }
    set
    {
        value.Store("SuccessMessage");
    }
}

The only caveat is when accessing methods on the object you are wrapping, you have to use 1 of 2 approaches to get at the underlying value…

// Directly access the underlying value
SuccessMessage.Value.Split(...);

// Assign the wrapped value to a temporary variable to force the implicit cast
string successMessage = SuccessMessage;
successMessage.Split(...);

As the session is really just a Dictionary<string, object> you could probably make this work for any dictionary with a few simple changes, however I didn’t need that flexibility so I leave it as an exercise for the future.

Categories: c#, WebForms

You know you are working on bad code when …

May 14, 2008 1 comment
  • There’s more orange in ReSharper’s gutter than there is grey.
  • Code analysis for the current file takes longer than solutionwide error analysis of your last “good” project.

I’m back to working on legacy code written by hamfisted monkeys with little regard for anything vaguely related to good coding practices – 2500+ line codebehind classes anyone!?!

Feel free to add other “You know you’re working on bad code” signs in the comments – I’m in desperate need of a laugh :s

Categories: General

Saying thanks…

May 2, 2008 Leave a comment

For the past couple of months I have been building a web based surveying application designed to allow employers to carry out lifecycle surveying of their employees (e.g. when they start, annual reviews, exit surveys).  When I started building this application, I was given complete control over how it was to be built and I chose to use MonoRail.  After a while I integrated Windsor, NHibernate and Rhino.Commons.  All of my unit tests have been written using NUnit.

After spending the previous 5 or so years fighting working with ASP.NET and various incarnations of MVP and MVC, hand writing data access code and stored procedures and not really leveraging any kind of dependency injection, using this stack has been bloody marvelous and a true breath of fresh air.  The learning curve is steep but rarely treacherous, and there is plenty of support in the community.  Even better,  I can peer into the inner working to see why something is (or isn’t) working.

To list all the contributors to those projects would require a rather long blog post (although you all deserve a huge round of applause), but there is generally one or two people who are “responsible” for these projects and spending large amounts of time and effort keeping them going.  So here’s a thank you to Hamilton Verissimo aka Hammett for the Castle stack ( MonoRail / Windsor and a whole lot more );  Karl Chu and Sergey Koshcheyev for NHibernate; Oren Eini aka Ayende for Rhino.Commons ( although his name appears a lot in conjunction with both Castle and NHibernate ); and Charlie Poole, Jamie Cansdale and Gary Feldman for NUnit.

All of these projects are the results of a large number of people donating their time and expertise, so I just wanted to throw a resounding THANK YOU! out into the ether in the hope that they might see it and know my gratitude.

Categories: General

MonoRail, Prototype validation and multiple submit buttons

May 2, 2008 3 comments

Previously I have discussed how I added action level filters to allow me to drop a cancel button on the page and worry about how the cancel was handled in the controller. This worked fine and did exactly as what I needed until I added client-side validation to the pages, the cancel button (being a submit button) triggered the validation.  To stop my cancel buttons triggering the validation I stopped the validator from automatically firing when the form was submitted by adding the following to the FormTag call:

$Form.FormTag( "%{action='processform', on_submit='false'}" )

To reconnect the validation and only validate the form if the button was not a cancel button, I used the following javascript that relies on the Prototype scripting library (I am using version 1.6.0.2).

/*
    Allows form validation while enabling the use of a submit button to
    cancel the page.
*/

// Register the validators once the form has loaded
Event.observe(window, 'load', RegisterValidationEventHandlers);

function RegisterValidationEventHandlers( event )
{
    if (window.prototypeValidators)
    {
        validatorsHash = $H(window.prototypeValidators);
        validatorsHash.values().each( function(value, index) {
            Event.observe(value.form, 'submit', validateFormIfNotCancelButton.bindAsEventListener(this));
        } );
    }
}

// Check if the event originated from a "cancel" button, if not proceed with normal validation
function validateFormIfNotCancelButton( event )
{
    var buttonClicked = document.activeElement || event.explicitOriginalTarget;
    var eventElementName = Element.readAttribute(buttonClicked, 'name');
    if(eventElementName != 'cancel')
    {
        var validator = prototypeValidators[event.element().id];
        validator.onSubmit( event );
    }
}

This first line registers an event listener on the window load event and executes the RegisterValidationEventHandlers function.

The RegisterValidationEventHandlers function checks for the existence of an associative array called prototypeValidators (automatically created by the PrototypeWebValidator) and if it is present, loops through attaching an event listener to the submit event for each form that has a validator. Note: for this to work, you will need to patch your PrototypeWebValidator as it currently creates an associative array from a normal array which is considered bad practice (see http://support.castleproject.org/browse/MR-440 for details).

The actual validation code is reasonably simple, retrieve the element that caused the event to fire, check it’s name and if it is not cancel, delegate the validation to the Prototype validator. The one interesting point to note is that there is no consistent mechanism to get the element that initiaited the submit, hence the var buttonClicked = document.activeElement || event.explicitOriginalTarget; which works in IE (document.activeElement) and FF (event.explicitOriginalTarget).

This script will work so long as you are using the default validation provider for MonoRail (prototype / dexagogo) and supports multiple forms without the need to add anything beyond suppressing the validation on submit behaviour via the additional property in the FormTag call.

Categories: MonoRail, Prototype
Follow

Get every new post delivered to your Inbox.