Blog Stats
  • Posts - 36
  • Articles - 2
  • Comments - 17
  • Trackbacks - 14

 

Got my nice Urls working

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:


Feedback

# re: Got my nice Urls working

Gravatar Two things, firstly you don't need routes of all the different lengths, by giving each level a default you can just have one:<br />new Route {<br /> Url = "C/[cat1]/[cat2]/[cat3]",<br /> Defaults = new {<br /> Controller = ..., Action=...,<br /> cat1 = (string)null,<br /> cat2 = (string)null,<br /> cat3 = (string)null<br />}<br />[...]<br /><br /><br />Second, incremental LINQ really needs to use non-comprehension expressions, then one can build them up incrementally:<br /><br />var res = source.Where(x =&gt; x.cat = cat1);<br />if (null != cat2) {<br /> res = res.Where(x =&gt; x.cat = cat2);<br />}<br /><br />Because of the delayed execution, this will generate a single SQL call, however many bits you use when breaking up the expression. 1/16/2008 2:47 PM | Richard

# re: Got my nice Urls working

Gravatar Im not sure if that would work well for the examples though, since the Url could be example/[cat1]/[cat2]/[example] or example/[cat1]/[cat2]/[cat3]/[example]. <br /><br />I'll have to take a look at the LINQ though.<br /><br /> 1/17/2008 3:15 PM | slynch

# Christopher Steen

Gravatar 1/2/2008 5:26 PM | dotnetjunkies.com

# I think I'm missing something

Gravatar 2/6/2008 3:30 AM | myheadsexploding.com

Post a comment





 

Please add 3 and 3 and type the answer here:

 

 

Copyright © Sean Lynch