Graduate Developer wanted

April 20th, 2012

Looking to join a young, dynamic company with plenty of ambition? Not keen on the idea of a stuffy corporate IT environment? Fancy the idea of working in vibrant East London? Want to work with the latest technologies and have big say in the direction of your company? Do you take pride in the work that you do? If this sounds like your cup of tea then we may have a role just for you.

Concurrent Development is an IT consultancy based in Bethnal Green specialising in building high quality web applications for a wide variety of companies from the music industry to green energy. Focusing on Microsoft .NET we like to offer our clients the latest, best of breed products and as such you will be working with cutting edge technologies, such as MVC, NHibernate and jQuery.

You will be joining a young, relaxed environment where we prize initiative and lateral-thinking above CVs full of worthy extra-curricular activities. We expect you to quickly become a key member of our team and you will rapidly gain exposure to all our clients and technologies as we drop you in at the deep end – we can guarantee you’ll learn more with us than anywhere else.

For more information please call us on 020 3487 0708 or email jobs@concurrentdevelopment.co.uk.

ASP.NET MVC LinkButton with HtmlHelper extensions

February 22nd, 2011

Download code

We’re really loving using ASP.NET MVC3 for so many reasons, not least the way in which Microsoft seem to have embraced externally developed tools such as jQuery. In Visual Studio the whole experience feels really cohesive and extending it is a breeze as this post will show.

Something we have found missing that seems an unusual oversight is an HtmlHelper extension method for submit buttons. Now I know it’s not much to write:

<input type="submit" value="sign in" />

but it would seem much neater to do this:

@Html.Submit("sign in")

(btw we’re using the razor view engine).

Html.Submit

It’s easy enough to write your own HtmlHelper extension methods for the submit button:

        /// <summary>
        /// Returns an HTML submit button
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <returns></returns>
        public static MvcHtmlString Submit(this HtmlHelper htmlHelper)
        {
            return htmlHelper.Submit("submit", null);
        }

        /// <summary>
        /// Returns an HTML submit button
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="value">The text for the button</param>
        /// <returns></returns>
        public static MvcHtmlString Submit(this HtmlHelper htmlHelper, string value)
        {
            return htmlHelper.Submit(value, null);
        }

        /// <summary>
        /// Returns an HTML submit button
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="value">The text for the button</param>
        /// <param name="htmlAttributes">The html attributes to apply to the button</param>
        /// <returns></returns>
        public static MvcHtmlString Submit(this HtmlHelper htmlHelper, string value, object htmlAttributes)
        {
            // init a tag builder
            TagBuilder tb = new TagBuilder("input");

            // get the attributes
            var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes) as IDictionary<string, object>;

            // set the attributes
            tb.MergeAttributes<string, object>(attributes);

            // set the type
            tb.MergeAttribute("type", "submit", true);

            // set the value
            tb.MergeAttribute("value", value);

            // return self closing
            return new MvcHtmlString(tb.ToString(TagRenderMode.SelfClosing));
        }

Html.SubmitImage

What about an input button (aka the old web forms ImageButton control), well that’s pretty straightforward too:

        /// <summary>
        /// Returns an input type=image
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="imageUrl">The url of the image</param>
        /// <returns></returns>
        public static MvcHtmlString SubmitImage(this HtmlHelper htmlHelper, string imageUrl)
        {
            return htmlHelper.SubmitImage(imageUrl, null);
        }

        /// <summary>
        /// Returns an input type=image
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="imageUrl">The url of the image</param>
        /// <param name="htmlAttributes">The html attributes to apply to the button</param>
        /// <returns></returns>
        public static MvcHtmlString SubmitImage(this HtmlHelper htmlHelper, string imageUrl, object htmlAttributes)
        {
            // init a tag builder
            TagBuilder tb = new TagBuilder("input");

            // get the attributes
            var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes) as IDictionary<string, object>;

            // set the attributes
            tb.MergeAttributes<string, object>(attributes);

            // set the type
            tb.MergeAttribute("type", "image", true);

            // set the image url
            tb.MergeAttribute("src", imageUrl);

            // set the alt attribute
            tb.MergeAttribute("alt", "submit");

            // return self closing
            return new MvcHtmlString(tb.ToString(TagRenderMode.SelfClosing));
        }

And you can now use this like so:

@Html.SubmitImage(Url.Content("~/url/to/image.png"))

Html.SubmitLink

Ok, so far so good, what about the good old LinkButton? I’ve seen several posts around the internet that we should be using ActionLink, but that doesn’t submit the form. So, using the principles of unobtrusive javascript we create the following extension:

        /// <summary>
        /// Returns an A tag with a nested SPAN used to submit a form, requires jquery.link-button.js
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <returns></returns>
        public static MvcHtmlString SubmitLink(this HtmlHelper htmlHelper)
        {
            return htmlHelper.SubmitLink("submit");
        }

        /// <summary>
        /// Returns an A tag with a nested SPAN used to submit a form, requires jquery.link-button.js
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="linkText">The text for the link button</param>
        /// <returns></returns>
        public static MvcHtmlString SubmitLink(this HtmlHelper htmlHelper, string linkText)
        {
            return htmlHelper.SubmitLink(linkText, null);
        }

        /// <summary>
        /// Returns an A tag with a nested SPAN used to submit a form, requires jquery.link-button.js
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="linkText">The text for the link button</param>
        /// <param name="htmlAttributes">The html attributes to apply to the button</param>
        /// <returns></returns>
        public static MvcHtmlString SubmitLink(this HtmlHelper htmlHelper, string linkText, object htmlAttributes)
        {
            // init a tag builder
            TagBuilder tb = new TagBuilder("a");

            // get the attributes
            var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes) as IDictionary<string, object>;

            // set the attributes
            tb.MergeAttributes<string, object>(attributes);

            // add the data attribute
            tb.MergeAttribute("data-link-button", "true");

            // add a nothing href
            tb.MergeAttribute("href", "#");

            // set the inner html
            tb.InnerHtml = String.Format("<span>{0}</span>", linkText);

            // return self closing
            return new MvcHtmlString(tb.ToString());
        }

You will notice that the markup rendered by this contains a span nested within the a tag. We implemented the extension method like this to make it easy for us to do style the buttons nicely using the sliding doors technique.

Now, this won’t work without the associated javascript file (jquery.link-button.js):

/// <reference path="jquery-1.5.js" />
(function ($) {
    $("a[data-link-button=true]").live("click", function (e) {
        e.preventDefault();
        var form = $(this).parents('form').first().submit();
        return false;
    });
})(jQuery);

As you can see this relies on jQuery so you’ll need a reference to that too.

Download code

New eCommerce solution partner Mediachase

February 4th, 2011

We are very pleased to announce our new partnership with Mediachase, a US based eCommerce solution provider with whom we have an already strong relationship off the back of our successful Faber Music projects. Read more about our new partnership on the Mediachase site.

Custom ValidationAttribute for comparing properties.

January 14th, 2011

We’re using ASP.NET MVC3 on our latest project and have been really impressed so far.  One of the coolest things I have come across is using DataAnnotations to decorate the model classes and having the framework wire up all the validation and error messages.  It is truly great – you can find out more at Scott Guthrie’s blog post on the subject (for MVC2 but the principles are the same).

There’s no built in support for comparing two properties though this has been addressed to a certain extent by the CompareAttribute in the System.Web.Mvc namespace in MVC3, David Hayden has blogged about it here.  This attribute allows you to specify that the value of two properties should be equal, but doesn’t help with greater than or less than operations.  This kind of thing would be really useful to specify that the StartDate of an Appointment should be before the EndDate.

So I have written my own custom comparison validation attribute for use with asp.net mvc in the style of the built in data annotations.

    /// <summary>
    /// Specifies that the field must compare favourably with the named field, if objects to check are not of the same type
    /// false will be return
    /// </summary>
    [AttributeUsageAttribute(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
    public class CompareValuesAttribute : ValidationAttribute
    {
        /// <summary>
        /// The other property to compare to
        /// </summary>
        public string OtherProperty { get; set; }

        public CompareValues Criteria { get; set; }

        /// <summary>
        /// Creates the attribute
        /// </summary>
        /// <param name="otherProperty">The other property to compare to</param>
        public CompareValuesAttribute(string otherProperty, CompareValues criteria)
        {
            if (otherProperty == null)
                throw new ArgumentNullException("otherProperty");

            OtherProperty = otherProperty;
            Criteria = criteria;
        }

        /// <summary>
        /// Determines whether the specified value of the object is valid.  For this to be the case, the objects must be of the same type
        /// and satisfy the comparison criteria. Null values will return false in all cases except when both
        /// objects are null.  The objects will need to implement IComparable for the GreaterThan,LessThan,GreatThanOrEqualTo and LessThanOrEqualTo instances
        /// </summary>
        /// <param name="value">The value of the object to validate</param>
        /// <param name="validationContext">The validation context</param>
        /// <returns>A validation result if the object is invalid, null if the object is valid</returns>
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            // the the other property
            var property = validationContext.ObjectType.GetProperty(OtherProperty);

            // check it is not null
            if (property == null)
                return new ValidationResult(String.Format("Unknown property: {0}.", OtherProperty));

            // check types
            if (validationContext.ObjectType.GetProperty(validationContext.MemberName).PropertyType != property.PropertyType)
                return new ValidationResult(String.Format("The types of {0} and {1} must be the same.", validationContext.DisplayName, OtherProperty));

            // get the other value
            var other = property.GetValue(validationContext.ObjectInstance, null);

            // equals to comparison,
            if (Criteria == CompareValues.EqualTo)
            {
                if (Object.Equals(value, other))
                    return null;
            }
            else if (Criteria == CompareValues.NotEqualTo)
            {
                if (!Object.Equals(value, other))
                    return null;
            }
            else
            {
                // check that both objects are IComparables
                if (!(value is IComparable) || !(other is IComparable))
                    return new ValidationResult(String.Format("{0} and {1} must both implement IComparable", validationContext.DisplayName, OtherProperty));

                // compare the objects
                var result = Comparer.Default.Compare(value, other);

                switch (Criteria)
                {
                    case CompareValues.GreaterThan:
                        if (result > 0)
                            return null;
                        break;
                    case CompareValues.LessThan:
                        if (result < 0)
                            return null;
                        break;
                    case CompareValues.GreatThanOrEqualTo:
                        if (result >= 0)
                            return null;
                        break;
                    case CompareValues.LessThanOrEqualTo:
                        if (result <= 0)
                            return null;
                        break;
                }
            }

            // got this far must mean the items don't meet the comparison criteria
            return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
        }

        /// <summary>
        /// Applies formatting to an error message.
        /// </summary>
        /// <param name="name">The name to include in the error message</param>
        /// <returns></returns>
        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentCulture, base.ErrorMessageString, name, OtherProperty, Criteria.Description());
        }

        /// <summary>
        /// retrieve the object to compare to
        /// </summary>
        /// <returns></returns>
        object GetOther(ValidationContext context)
        {
            return null;
        }
    }

    /// <summary>
    /// Indicates a comparison criteria used by the CompareValues attribute
    /// </summary>
    public enum CompareValues
    {
        [Description("=")]
        EqualTo,
        [Description("!=")]
        NotEqualTo,
        [Description(">")]
        GreaterThan,
        [Description("<")]
        LessThan,
        [Description(">=")]
        GreatThanOrEqualTo,
        [Description("<=")]
        LessThanOrEqualTo
    }

To use this attribute one would then just do:

    /// <summary>
    /// Represents a booking
    /// </summary>
    public class Booking : DomainObject<int>
    {
        /// <summary>
        /// The start of the booking
        /// </summary>
        [Required]
        [CompareValues("End", CompareValues.LessThan)]
        public virtual DateTime? Start { get; set; }

        /// <summary>
        /// The end of the booking
        /// </summary>
        [Required]
        public virtual DateTime? End { get; set; }
    }

Muse Sheet Music Download Store Launched

October 5th, 2010

Today sees the launch of another ePartner sheet music download store.  This one is for the band Muse and can be found here: http://musesheetmusicdownloads.epartnershub.com/

It is very exciting to see our hard work paying off for our client Faber Music – our custom built management systems allowing them to launch new ePartner sites almost as soon as a design is finalised.

Looking for a Graduate Job?

August 26th, 2010

Are you a graduate looking for a job?  Do you want a career in IT?  If so, we are interested in hearing from you.  Check out the advert on our jobs / careers page.

NHibernate, SchemaUpdate and MySQL LONGTEXT

August 25th, 2010

After spending a long time working on an NHibernate project where SchemaUpdate was not working against a MySQL database I spent some time looking into the issue.  No errors were being spat out, I wasn’t even attempting to execute against the DB, just generate the script.  It just looked like my DB was in synch with my mappings.  It wasn’t.

With much scratching of head and several hours of trial and error I finally discovered this one irritating fact:  If you have a table in your MySQL database that contains a mapped column of type LONGTEXT, don’t expect SchemaUpdate to work.  TEXT works fine, so does MEDIUMTEXT (thank the maker – as TEXT is not long enough for this project), but not LONGTEXT.

Grrrr!

We have moved!

May 13th, 2010

As of this week, we now work out of our new offices in the East End of London. Our new address is:

15 – 20 The Oval
London
E2 9DX

2 minute walk from Cambridge Heath rail
5 minute walk from Bethnal Green underground

First EPartner Site Launched

December 16th, 2009

We take great pleasure in announcing the launch of the first website in the Faber Music EPartners scheme, MusicRadar’s guitar tab download store.

We have been working in partnership with Faber Music for the past three years on all the backend systems and catalogue management tools and building the ecommerce system using Mediachase’s ECF platform was the final step to bringing our hard work into the public domain.

The EPartners scheme is centred around a hub of legal and approved downloadable sheet music. Any EPartner can sell any item and any EPartner can contribute sheet music to the hub. So if you’ve got a burning desire to sell your accordion arrangement of Welcome to the Jungle then EPartners scheme is for you.

More EPartners sites will be coming in the New Year along with a new Faber Music site and store.

LinkButtons, UpdatePanels and IDs

June 2nd, 2009

I have an Repeater control sitting on my page, in the ItemTemplate of which are a couple of Button controls which do various things – delete the item, open the item’s detail pages etc… I think it would be nice to wrap the whole thing in an UpdatePanel to give it that whole AJAX-y feel. All good.

Then the client asks if the buttons can be links instead of buttons. No problem, think I, and go ahead and change the Buttons to LinkButtons. At which point the application stops working. Clicking on the link buttons does a full post back (even though they’re in the UpdatePanel) and their associated methods in the code do not fire. Very strange. I remove the UpdatePanel and the methods fire. All very odd.

Now, I don’t like giving IDs to controls that I don’t reference directly in the code. So as a matter of course I remove IDs from things like Buttons, LinkButtons etc… Which is all fine until you put a LinkButton in an UpdatePanel – where an ID is necessary.

So, if you have a LinkButton within an UpdatePanel and it is doing an unexpected post back and the onclick event is not firing, check that you have an ID on it.