Sean Lynch

March 18, 2008

Got my nice Urls working

Filed under: .Net,Development,MS Mvc — Sean Lynch @ 12:52 am

Its amazing how fast things can be done when not fighting bad assumptions. In this case it was that my admin URLs needed to use the nice descriptive URLs, instead of just /[Controller]/[Action]/[id] type routing.

Once I realized I was making things overly complicated I, I ended up with a set of routing functions that are actually a bit less complicated then what I would have come up with if I had been able to extend the RouteTable the way I had planned originally.

I also decided that I didn’t like the idea of having the make every category and example name unique. Most of this was that I’m not the creative, but I also realized that it would be really ugly I wanted to have namespaces as categories. For example, it would not have been possible to have both C/System/Windows/Forms/Control and C/System/Web/UI/Control since Control would have had to be unique.

I also decided that I was going to set a limit to the depth of categories that the site was going to have, though it is easy to update this. First I made a function that would generate routes similar to those previously posted.

protected void CreateRouteSet(string baseRoute, string baseName
    , int maxDepth, object Defaults)
{
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < maxDepth; i++)
    {
        if (sb.Length > 0)
            sb.Append("/");

        sb.AppendFormat("[{0}{1}]", baseName, i);

        RouteTable.Routes.Add(new Route{

            Url = String.Format(baseRoute,sb.ToString()),
            Defaults = Defaults,
            RouteHandler = typeof(MvcRouteHandler)
        });
    }
}

protected void Application_Start(object sender, EventArgs e)
{

    CreateRouteSet("C/{0}", "Category", 6,
        new { Action = "Index", Controller = "Categories" }
        );

    //Other routes using the standard [Controller]/[Action]/[Id]

}

This will setup routes to handle category hierarchies up to 6 deep.  And add Category1,Category2… into the RouteData.Values collection in the controller.

Next I set up the code to allow me to handle the route, which I did in the model, adding a method that takes an IDictionary<string,object>.

public Category GetCategoryFromRouteData(IDictionary<String,object> data)
{
    //Grab just the category data    var navList = from route in data
               where route.Key.IndexOf("Category") == 0
               orderby route.Key
               select new{Key = route.Key, Value = route.Value.ToString()};

    string sqlSelect = @"
        SELECT {0}.*
        FROM Category {0}
        {1}
        WHERE {0}.Name = {{0}}";
        //{{{2}}} will be replaced with {{<param index>}}    string join = @"INNER JOIN Category {0} ON {0}.[Key] = {1}.ParentKey
                    AND {0}.[Name] = {{{2}}}

    //This is the name of the category I am actually interested in    var mainCat = navList.Last();

    //Build a list of category values to for parameterized query    List<string> values = new List<string>();
    values.Add(mainCat.Value);
    StringBuilder sb = new StringBuilder();
    string lastCategory = mainCat.Key;

    int i = 1;
    foreach (var item in navList.Reverse().Skip(1))
    {
        sb.AppendFormat(join, item.Key, lastCategory,i++);
        values.Add(item.Value);
        lastCategory = item.Key;
    }

    string sql = String.Format(sqlSelect,mainCat.Key, sb.ToString());
    Category cat = this.ExecuteQuery<Category>(sql, values.ToArray()).Single();


    //GetDisplayCategory skips over categories that have only 1 subcategory     //and no examples, allowing me to have the planned Url structure without     //requiring the user to navigate through several empty categories.    return GetDisplayCategory(cat);
}

I had planned on having doing it all LINQ to SQL expressions, but it was taking more time then it was worth to figure out how to do the hierarchy lookup from the category names.  I need to add some caching of the key lookups at some point, since the joins look like they might get a bit nasty for deep categories, but that can wait.

I added a few overloads to the HtmlHelper ActionLink extension methods to handle making the nice Url links.

Now I need to figure out how to deploy the usercontrols that will contain the executable part of the example. I have had some problems with compilation sometimes when I had the website directly upload the usercontrols. Plus I would like them to go into source control when I add them.

Technorati Tags:

No Comments »

No comments yet.

RSS feed for comments on this post. TrackBack URL

Leave a comment

Powered by WordPress