views:

714

answers:

2

How would you tackle this problem:

I have data in my data store. Each item has information about:

  • URL = an arbitrary number of first route segments that will be used with requests
  • some item type = display will be related to this type (read on)
  • title = used for example in navigation around my application
  • etc.

Since each item can have an arbitrary number of segments, I created a custom route, that allows me to handle these kind of requests without using the default route and having a single greedy route parameter.

Item type will actually define in what way should content of a particular item be displayed to the client. I was thinking of creating just as many controllers to not have too much code in a single controller action.

So how would you do this in ASP.NET MVC or what would you suggest would be the most feasible way of doing this?

Edit: A few more details

My items are stored in a database. Since they can have very different types (not inheritable) I thought of creating just as many controllers. But questions arise:

  1. How should I create these controllers on each request since they are related to some dynamic data? I could create my own Controller factory or Route handler or possibly some other extension points as well, but which one would be best?

  2. I want to use MVC basic functionality of using things like Html.ActionLink(action, controller, linkText) or make my own extension like Html.ActionLink(itemType, linkText) to make it even more flexible, so Action link should create correct routes based on Route data (because that's what's going on in the background - it goes through routes top down and see which one returns a resulting URL).

  3. I was thinking of having a configuration of relation between itemType and route values (controller, action, defaults). Defaults setting may be tricky since defaults should be deserialized from a configuration string into an object (that may as well be complex). So I thought of maybe even having a configurable relation between itemType and class type that implements a certain interface like written in the example below.

  4. My routes can be changed (or some new ones added) in the data store. But new types should not be added. Configuration would provide these scenarios, because they would link types with route defaults.

Example:

Interface definition:

public interface IRouteDefaults
{
    object GetRouteDefaults();
}

Interface implementation example:

public class DefaultType : IRouteDefaults
{
    public object GetRouteDefaults()
    {
        return new {
            controller = "Default",
            action = "Show",
            itemComplex = new Person {
                Name = "John Doe",
                IsAdmin = true
            }
    }
}

Configuration example:

<customRoutes>
    <route name="Cars" type="TypeEnum.Car" defaults="MyApp.Routing.Defaults.Car, MyApp.Routing" />
    <route name="Fruits" type="TypeEnum.Fruit" defaults="MyApp.Routing.Defaults.Fruit, MyApp.Routing" />
    <route name="Shoes" type="TypeEnum.Shoe" defaults="MyApp.Routing.Defaults.Shoe, MyApp.Routing" />
    ...
    <route name="Others" type="TypeEnum.Other" defaults="MyApp.Routing.Defaults.DefaultType, MyApp.Routing" />
</customRoutes>

To address performance hit I can cache my items and work with in-memory data and avoid accessing the database on each request. These items tend to not change too often. I could cache them for like 60 minutes without degrading application experience.

+2  A: 

The general concept you mention is not at all uncommon and there are a few things to consider:

  1. The moment I hear about URL routing taking a dependency on data coming from a database, the first thing I think about is performance. One way to alleviate potentialy performance concerns is to use the built in Route class and have a very generic pattern, such as "somethingStatic/{*everythingElse}". This way if the URL doesn't start with "somethingStatic" it will immediately fail to match and routing will continue to the next route. Then you'll get all the interesting data as the catch-all "everythingElse" parameter.

  2. You can then associate this route with a custom route handler that derives from MvcRouteHandler and overrides GetHttpHandler to go to the database, make sense of the "everythingElse" value, and perhaps dynamically determine which controller and action should be used to handle this request. You can get/set the routing values by accessing requestContext.RouteData.Values.

  3. Whether to use one controller and one action or many of one or many of each is a discussion unto itself. The question boils down to how many different types of data do you have? Are they mostly similar (they're all books, but some are hardcover and some are softcover)? Completely different (some are cars, some are books, and some are houses)? The answer to this should be the same answer you'd have if this were a computer programming class and you had to decide from an OOP perspective whether they all have a base class and their own derives types, or whether they could be easily represented by one common type. If they're all different types then I'd recommend different controllers - especially if each requires a distinct set of actions. For example, for a house you might want to see an inspection report. But for a book you might want to preview the first five pages and read a book review. These items have nothing in common: The actions for one would never be used for the other.

  4. The problem described in #3 can also occur in reverse, though: What if you have 1,000 different object types? Do you want 1,000 different controllers? Without having any more information, I'd say for this scenario 1,000 controllers is a bit too much.

Hopefully these thoughts help guide you to the right solution. If you can provide more information about some of the specific scenarios you have (such as what kind of objects these are and what actions can apply to them) then the answer can be refined as well.

Eilon
Ok so in #2 you're describing something like deciding controller/actions on the go. Ok. But how would you use Html.ActionLink then? They use Route data as well.
Robert Koritnik
Helpers such as Html.ActionLink work exactly the same. When you use that helper it will go through every route in the route collection until it finds one that matches. If you have the right set of constraints and/or default values and/or parameterized segments the matching will work all the same. Granted, if you something *very* custom it might make more sense to give the route a name (an optional parameter when you call Add), and then you can use Html.RouteLink() and specify the name. This causes the routing to jump straight to the one named route instead of going through the whole list.
Eilon
+3  A: 

There is no significant performance issue if you define a complex routing dictionary, or just have one generic routing entry and handle all the cases yourself. Code is code

Even if your data types are not inheritable, most likely you have common display patterns. e.g.

  • List of titles and summary text
  • item display, with title, image, description
  • etc

If you can breakdown your site into a finite number of display patterns, then you only need to make those finite controllers and views

You them provide a services layer which is selected by the routing parameter than uses a data transfer object (DTO) pattern to take the case data and move it into the standard data structure for the view

TFD
Yes I do have a finite number of display patterns. And I intended to do just that. create just as many controllers to handle these, because these controllers actually handle a certain navigation subtree. The thing is I can't hardcode routes. These navigation subtrees do have a TypeId that would help me. I'd define a configuration that connects certain controllers with TypeId. Routes can change any time, types don't. Is this feasible?
Robert Koritnik
It may be simple to implement your own IRouteHandler and make a ProcessRequest method that does all the route/type mappings via optimised code rather that RouteData dictionary - see http://weblogs.asp.net/fredriknormen/archive/2007/11/18/asp-net-mvc-framework-create-your-own-iroutehandler.aspx
TFD