Tuesday, July 28, 2009

Constraint cannot be special class 'System.Enum'

My esteemed colleague and all-round nice guy Dan Esparza came up with a great suggestion for an improvement to my ASP.NET MVC DropDownList from Enum post from yesterday. His idea, which I was very excited about, was:

Couldn't you use syntax similar to:
public static IDictionary<int, string> EnumToDictionary(this Type type) where T: Enum
Then, your extension method should only appear on Enum's

To test out anything like this I love to do red/green unit testing. i.e. demonstrate that it fails first, fix it and then demonstrate that it passes next. The pattern here is slightly different inasmuch as I wanted to demonstrate that the function compiles and runs as previously designed and then change the design and see the compile fail and not the execution of the unit test - i.e. the unit test should not even be able to compile this time - that would be the success of the test.

In the previous post I had negligently forgotten to add a unit test to test for the exception being thrown in the extension method that I wrote. So I have added one:

[TestMethod]
[ExpectedException(typeof(InvalidCastException), "Attempted to use an invalid type.")]
public void test_enum_to_dictionary_exception()
{
    IDictionary<int, string> actual = typeof(Int32).EnumToDictionary();
}

I ran the unit test and it passed demonstrating that the original functionality threw an exception (and the right type of exception) when called with an invalid type. So far so good.

Next I went back to the extension method and changed its signature to read:

public static IDictionary<int, string> EnumToDictionary<T>(this T type) where T : Enum

...and compiled the project only to be rudely told: Constraint cannot be special class 'System.Enum'

More investigation shows the C# 2.0 specification to have the following comments on constraints:

A class-type constraint must satisfy the following rules:

  • The type must be a class type.
  • The type must not be sealed.
  • The type must not be one of the following types: System.Array, System.Delegate, System.Enum, or System.ValueType.
  • The type must not be object. Because all types derive from object, such a constraint would have no effect if it were permitted.
  • At most one constraint for a given type parameter can be a class type.

So that brought that idea to a screeching halt. Microsoft provide a workaround to this limitation by suggesting the use of a struct in place of the enum to increase the compile-time failures when using inappropriate types. However I could not get this to compile using my extension method pattern. This could just be my personal limitation and lack of depth in understanding generics and C# and a superior programmer may way be able to demonstrate the application of this workaround this to me...

Monday, July 27, 2009

ASP.NET MVC DropDownList from Enum

I sometimes have the requirement to create a drop down list on a web page from an Enum. Each time I do this I seem to jump through the same set of hoops and it always seems to take longer than it should to code this up.

To simplify this procedure I need the Enum converted to an IDictionary<int, string> which is easy to drop into an MVC View. To get there I turned to a ubiquitous project that I include in almost all solutions that I work on: The Extensions Project:

Here is the extension method that I wrote to convert an Enum to an IDictionary<int, string>:

/// <summary>
/// Converts an enum type to an IDictionary&lt;int,string&gt;
/// Although this will appear as an extension method on all type objects
/// it will throw an exception on all types except enum
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IDictionary<int, string> EnumToDictionary(this Type type)
{
    if (!type.IsEnum)
    {
        throw new InvalidCastException("The EnumToDictionary() extension method can only be used on types of enum. All other types will throw this exception.");
    }

    // The Enum.GetValues() function returns an array of objects which are actually ints,
    // that's why we cast them to ints before calling the ToDictionary function.
    // The ToDictionary() extension method takes two lambda expressions each of which
    // returns the type that corresponds to the dictionary's types. Because we've cast
    // the array to ints the first param in the ToDictionary is used as is. The second
    // param gets the name of the Enum.
    Dictionary<int, string> dictionary =
        Enum.GetValues(type).Cast<int>().ToDictionary(a => a, a => Enum.GetName(type,a));

    return dictionary;
}

Note that I have to make sure that someone doesn't try and call this on a type that's not an Enum because this will show up as an extension method in intellisense for all types and not just enum types.

Here is the unit test and an example of how to use the extension method:

[TestMethod]
public void test_enum_to_dictionary_of_int_and_string()
{
    IDictionary<int, string> actual = typeof(UnitTestEnum).EnumToDictionary();
    Assert.AreEqual(4, actual.Count);
    Assert.AreEqual("Five", actual[5]);
    Assert.AreEqual("Ten", actual[10]);
    Assert.AreEqual("Fifteen", actual[15]);
    Assert.AreEqual("Twenty", actual[20]);
}

Example enum in unit test:

enum UnitTestEnum { Five = 5, Ten = 10, Fifteen = 15, Twenty = 20 };

In the ASP.NET MVC project, in the Action method of the Controller I create a view model for my view and set the dictionary property on that view model with the newly created extension method:

MyViewModel mvm = new MyViewModel();
mvm.MyTypes = typeof(MyEnum).EnumToDictionary();

This model is then passed into the view by returning it as the second parameter to the View() call...

return View("MyView", mvm);

...and in the view page I have the following:

<% =Html.DropDownList("MyType", new SelectList(Model.MyTypes, "Key", "Value")) %>

In the MyViewModel object I also have an Enum property:

public MyEnum MyType { get; set; }

Note that the enum variable for MyEnum is named MyType. This matches the first paramter to the Html.DropDownList() helper method. The reason that this is important is because when you receive a POST to your controller and you call the UpdateModel() method this will cast the value returned from the drop down to the Enum for you.

This happens because the Html.DropDownList() helper generates a <select> html input field like this:
<select id="MyType" name="MyType">...</select>
so when the form POSTs back the named form value is "MyType" and the MVC UpdateModel() call finds this value when iterating through the public properties of the MyViewModel class and knows that it has to cast it to a MyEnum type because that's the type in the class.

Thursday, July 23, 2009

The HTML <button>

I was getting a bit confused about some of the attributes on the HTML <button> element so I thought that I'd write them out to clear it in my mind.

Here's the example:

<button name="submitName" value="submitValue">Submit</button>

In the Action on the Controller (of the ASP.NET MVC app) the following statement:

            string submit = Request.Form["submitName"];

will set submit to that value of "submitValue"

i.e. name/value pair.

"submitName" in the Request.Form is not case sensitive. You can put "SubMitnaMe" and it will still find the value by that name/key.

Sunday, July 19, 2009

Defeating Spam Bots with country selection

Something that all membership based web sites have to face is the registration of users by spam bots in order to spam the site. One of the reasons that you frequently require site registration is to prevent spam bots from spamming your site. So the first line of defense is to force registration on the site which eliminates a handful of bots. However, as we all know, spam bots are getting pretty good at auto-registering on sites before spamming the site.

One solution is CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart) which is usually in the form of an obscured word that is difficult for an algorithm to see but easier for a human to read. There are other types of CAPTCHA's such as math tests and images of animals in which you have to click on the odd animal out.

One form of CAPTCHA that I have not seen (yet) that might be effective is that of making the registering user select the country that they are registering from. Using the user's IP address you can find the country (most of the time) from an online service or for a table in your database if you've downloaded (bought) this information. (I know that Melissa data provides this.) You then present the user with a radio button list of (say) 10 countries and ask them to select their country. You'd also probably have to throw in a "Not Listed" option for when you haven't been able to find the country. I would suggest throwing in the "Not Listed" option every time because if you only throw that in when you can't find the country then spam bots will be quickly tuned to look for this.

Now the reason that this may work is because spam bots are frequently running on Zombie machines from many different countries. This is why banning an IP doesn't prevent a bot from re-registering on your site from another Zombie machine at another IP address. So for the Zombie bots to be able to detect what country they are coming from they'd need to have an IP address lookup table which they are unlikely to have.

Another enhancement to the presentation of country names that would still be easy for a real user to navigate and difficult for a spam bot would be to present the country name as an obscured image (like words are currently obscured in standard CAPTCHA's), or present the country's flag, or an outline (map) of the border of the country, or the country's national colors, or one of the country's national animals such as its bird. You would need to make sure that you coded for ambiguity in some of the previous examples if two countries shared the same characteristic.

Saturday, July 11, 2009

Online Sprite Generator

 I've just found and used this: Online Sprite Generator and wanted to make a note of it before I lost it and could never find it again.

It's very easy to use. Take all your icons (or any images) that you have on your site and put them into a single zip. Upload them to the site and generate the CSS and single image file. You can download the single image file and copy paste the CSS that's generated for you. It took me all of 10 minutes to do a global find and replace for the CSS names and it worked immediately. I left all the options on the generator web site at their defaults.

Thursday, July 9, 2009

Adding classes to elements to assist jQuery

As I start using jQuery more and more I'm finding myself wanting to select elements on a page based on criteria that original come from the database records that generated the data for those elements.

I am currently working on a project to convert classic ASP Snitz forum software to ASP.NET MVC and I'm moving all the JavaScript to jQuery while I'm doing this. As with most forums, this forum has a subscription option which allows the user to subscribe to the entire board, a category on the board, a forum in a category, or an individual topic.

As such, subscribe and unsubscribe icons and anchor text litter most pages. My first attempt at coding this had most of the which-icon-do-we-display logic on the server and then jQuery ajax changed the buttons and performed actions when these icons were clicked which caused small amounts of code to be duplicated in JavaScript and C#.

My current iteration has the server mark the elements with classes and then have all the manipulation done on the client using jQuery. I've identified 4 classes which are synonymous with data fields that I'm using to find the right elements:

CollectionType: This can only hold the value subscription and identifies this element as an anchor for subscriptions.

DataType: Can be one of board, category, forum, or topic. Identifies the type (or level) of subscription.

Active: Can be one True or False. Note the proper case. These values are generated by the toString() function from booleans.

ID: This is a number such as 4102 which is the ID of the category, forum or topic to identify the exact record in the DB. The data type of board would not have anything here.

If I want to find all of the subscription icons on a page that are active then I can use:

$('.subscription.True')

all topics would be:

$('.subscription.topic')

and a specific topic would be:

$('.subscription.topic.4102')

It feels wrong to be using ID's as classes but this certainly seems to make the query part of jQuery work very well. Likewise, putting 'True' and 'False' in as css classes seems wrong.

Anybody have a better solution for this?

Tuesday, July 7, 2009

RouteLink and ActionLink in ASP.NET MVC

For some reason this took me some time to understand.

RouteLink() has 10 overloads. For some reason I couldn't understand that RouteLink went back to the routes.MapRoute() that you originally setup in the global.asax.cs file. That I understand now. The overload that I am currently using the most is:

string HtmlHelper.RouteLink(string linkText, string routeName, string routeValues);

e.g.:

RouteLink("Text that will appear on page", "First param in MapRoute()", new { controller = "Home", action = "Index", id = Model.ID })

ActionLink() on the other hand, I'm now using to route to another action on that same controller or the same action with different parameters.