Showing posts with label Extension Methods. Show all posts
Showing posts with label Extension Methods. Show all posts

Wednesday, 12 January 2011

Asp.net MVC2 + HTML5 data-* attributes

So you want to add some custom data to a html element to aid your JavaScript development and adding data values to custom attributes is a big no no so what do you do? This is where the new HTML5 data-* comes to your rescue.

The next issue is you can’t use the usual ActionLink method overloads on the html helper as you can’t pass in ‘-‘ into anonymous types because it is a c# reserved character for subtracting!

So on the train home this evening I came up with the following:

public static MvcHtmlString ActionLinkHtml5Data(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes, object htmlDataAttributes)
{
    if (string.IsNullOrEmpty(linkText))
    {
        throw new ArgumentException(string.Empty, "linkText");
    }

    var html = new RouteValueDictionary(htmlAttributes);
    var data = new RouteValueDictionary(htmlDataAttributes);

    foreach (var attributes in data)
    {
        html.Add(string.Format("data-{0}", attributes.Key), attributes.Value);
    }

    return MvcHtmlString.Create(HtmlHelper.GenerateLink(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, null, actionName, controllerName, new RouteValueDictionary(routeValues), html));
}

And to use it, you already know how to as its the same way as all the other ActionLink overrides which are already available out of the box with an additional parameter which works the same as the htmlattributes parameter.

<%: Html.ActionLinkHtml5Data(“link display”, "Action", "Controller", new { id = Model.Id }, new { @class="link" }, new { extra = “some extra info” })  %>

It worked like a dream.

The issue with the “-“ being reserved has had a work around in MVC 3 by using “_” and then programmatically changing that to being “-“ in the output. I’ve not looked at the implementation but I can’t see how this is much different to what I’ve already achieved simply above.

Please let me know your thoughts.

Wednesday, 17 March 2010

SqlParameterCollection Extension Method AddWithValue

I don’t know why it’s not included in .net 3.5 / c#3 but the recommended way of adding a SQL Parameter to a SqlParameterCollection is to use the AddWithValue method; however, you can’t specify the SqlDbType with any of the overloads. It’s not hard …

    public static class DataAccessExtensions
{
/// <summary>
/// AddWithValue specifiying the <see cref="SqlDbType"/> of the parameter
/// </summary>
/// <param name="collection">The collection to add the parameter to</param>
/// <param name="parameterName">The name of the parameter</param>
/// <param name="value">The value of the parameter</param>
/// <param name="sqlDbType"><see cref="SqlDbType"/> which the SqlCommand is expecting for the created parameter</param>
public static void AddWithValue(this SqlParameterCollection collection, string parameterName, object value, SqlDbType sqlDbType)
{
var result
= new SqlParameter(parameterName, sqlDbType) { Value = value };
collection.Add(result);
}
}



Friday, 2 January 2009

IEnumerable Extension Methods

After looking over some questions on StackOverflow it got me thinking about extension methods. It's not really something which I have looked into much as I only use Visual Studio 2008 at home for personal projects. I stumbled upon one post about potential extension methods to add to a colaboration on CodePlex for everyones use. I was scrolling through some of the potentials to be submitted and came across this post. It intrigued me as the main method was converting an IEnumerable to a string with a separator ... so would convert an array of ints 1,2,3 into the string "1, 2, 3" if the separator was ", " ... I think you get the point.

However the way they were doing it was the usual "go through all the items, add the separator after everyone, then at the end take the last off". Personally when I see this I get a little sad. It's just not very elegant but also it means you don't harness the power of the framework ... which is what it is there for!

Side Note With the advent of .net 2.0 Generics was brought in. This opened up a whole world of fun for developers and a lot of useful things especially around the area of collections. If you've not looked at Generics I would highly recommend working your Google-Fu and doing some reading.

Anyway ... using generics I decided to refactor the ToString method for IEnumerable to use ToArray() of a List and string.join(). This might be a new way for some people of concatenating values together with a supplied separator. It leverages the power of the .net framework to do the work and avoids having to add an extra separator and then removing it once you have iterated through all the items in the list and performing the specified delegate function.

        public static string ToString<T>(this IEnumerable<T> collection,
Func<T, string> stringElement, string separator)
        {
            List<string> result = new List<string>();
            foreach (T item in collection)
            {
                result.Add(stringElement(item));
            }
            return string.Join(separator, result.ToArray());
        }


So moving on from this I thought that a List has a ForEach extension method which executes a delegate on each of the elements in the list; but not on an IEnumerable. This has been discussed and documented in so many places I'm not going to write about it here.  So what I wanted to do was break out the ForEach functionality out of the ToString implementation so it can be used injunction with it, but also by itself.


        public static IEnumerable<T> ExecuteForEach<T>(this IEnumerable<T> collection,
Func<T, T> function)
        {
            List<T> result = new List<T>();
            foreach (T item in collection)
            {
                result.Add(function(item));
            }
            return result.AsEnumerable();
        }


I made it return an IEnumerable<T> so it could be piped together to keep with the design of linq and lambda expressions etc. The limitation of this implementation is that it returns the same type as the type passed into the Func so you can't change the type, such as converting int to string. So take two came along ...


        public static IEnumerable<U> ExecuteForEach<T, U>(this IEnumerable<T> collection,
Func<T, U> function)
        {
            List<U> result = new List<U>();
            foreach (T item in collection)
            {
                result.Add(function(item));
            }
            return result.AsEnumerable();
        }


Same as the previous example, but with two different generic types, this enables the conversion of int to string, but also performing actions on the same types and returning the updated values eg. increment a list of ints.

With using the updated ExecuteForEach<T, U> (I decided on calling it ExecuteForEach as it will update / perform a function on each of the items in the collection and update them, not just perform an function on them) we can now update the ToString method call to be just two lines;  it abstracts the looping away into a different function which can be used again for different actions, but also uses the power of the framework with the string.join functionality.


        public static string ToString<T>(this IEnumerable<T> collection,
Func<T, string> stringElement, string separator)
        {
            List<string> possible = collection
.ExecuteForEach(t => stringElement(t))
.ToList();
            
            return string.Join(separator, possible.ToArray());
        }


A simple usage of this is ...


        public static string ToString<T>(this IEnumerable<T> collection, string separator)
        {
            return ToString(collection, t => t.ToString(), separator);
        }


... and the unit test to go with it ...


        [TestMethod]
        public void ToStringTest()
        {
            var ints = new int[] { 1, 2, 3 };
            var result = ints.ToString(", ");
            Assert.AreEqual("1, 2, 3", result);
        }


Hope this all makes sense, please let me know if you have any comments or thoughts about this :-)