2
\$\begingroup\$

I'm currently developping an MVC framework on which I will be writing my websites. This is done through the usage of a starter kit.

In standard MVC, your routes are registered like the following:

/// <summary>
///     Registers the routes.
/// </summary>
/// <param name="routes">The <see cref="RouteCollection" /> to which the routes will be added.</param>
public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    // All the required routes are placed here.
    routes.MapRoute("Website", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}

But since it's a framework, there's a need to add routes to the collection, so I started with an interface:

public interface IMvcRouteExtender
{
    #region Methods

    /// <summary>
    ///     Register routes for the application.
    /// </summary>
    /// <param name="routes">The <see cref="RouteCollection"/> that contains all the required routes.</param>
    void RegisterRoutes(RouteCollection routes);

    #endregion
}

Now, in an implementation class, I can do the following:

public class CustomRouteConfiguration : IMvcRouteExtender
{
    #region IMvcRouteExtender

    /// <summary>
    ///     Registers the routes.
    /// </summary>
    /// <param name="routes">The <see cref="RouteCollection" /> to which the routes will be added.</param>
    public void RegisterRoutes(RouteCollection routes)
    {
        routes.MapRoute("Default", "pensions/save-and-pension", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
    }

    #endregion
}

In order to be sure there available, the original routeconfig is changed so that it searches for those interfaces and register them.

/// <summary>
///     Registers the routes.
/// </summary>
/// <param name="routes">The <see cref="RouteCollection" /> to which the routes will be added.</param>
public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    // Get all the types in the references assembly that implement the 'IMvcRouteExtender' interface.
    var types = AppDomain.CurrentDomain.GetAssemblies().ToList()
                .SelectMany(a => a.GetTypes())
                .Where(t => typeof(IMvcRouteExtender).IsAssignableFrom(t) && !t.IsInterface);

    // Register all the routes.
    types.Select(type => Activator.CreateInstance(type) as IMvcRouteExtender).Each(x => x.RegisterRoutes(routes));

    // All the required routes are placed here.
    routes.MapRoute("Website", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}

As you'll see above, I'm using reflection to load the types in which the routes are defined. This is working fine, but I don't like the overhead of using reflection.

Is there anyone who has an idea on how to make this more efficient? AKA, remove the dynamic type loading?

Since I'm writing a framework I cannot have a reference between routeConfig and CustomRouteConfig.

Also: Don't look at code semantics here, I'm just talking about a general idea to make the code more performant.

\$\endgroup\$
5
  • 2
    \$\begingroup\$ If I recall correctly, the RouteCollection is fairly static (life of the app pool?), so this shouldn't actually add significant amounts of overhead. This is exactly how I have & will continue to implement requirements of "I need to add xyz at will and it 'just work'". What areas of inefficiencies are you concerned with? Asp Life cycle \$\endgroup\$
    – AWinkle
    Aug 18, 2014 at 16:59
  • \$\begingroup\$ I'm just scanning for all the types that are implementing an interface. In very huge projects, this scanning process might take some time which will affect the performance. Not noticeable, but I would like to now if there are cleaner ways. \$\endgroup\$
    – Complexity
    Aug 18, 2014 at 18:02
  • 2
    \$\begingroup\$ I use very similar approaches in my mvc websites for composition root and ioc build up. Everyone seems to have it in their head that reflection is inherently slow. Ignoring that your code will only run once per appdomain as @AWinkle remarks, have you actually stuck a timer around this and noticed a performance issue? I'd bet a virtual cookie the time it takes is bur the tiniest of fractions compared to the time it takes for your website to start without that code? \$\endgroup\$
    – Smudge202
    Aug 20, 2014 at 21:21
  • \$\begingroup\$ I think @Smudge202 is on the right path. Sometimes, as developers, we optimize to death things could be bad before proving that they might be bad. \$\endgroup\$
    – AWinkle
    Aug 21, 2014 at 14:18
  • \$\begingroup\$ I thinks you're right. Thanks for the valuable help. \$\endgroup\$
    – Complexity
    Aug 21, 2014 at 15:27

1 Answer 1

2
\$\begingroup\$

It was more or less worked out in the comments that the use of reflection is a fine way to go about it and I agree with that. What I can't understand is your use of regions. In IMvcRouteExtender you have one that wraps a single method. In my opinion, it just clutters the code. In your implementation, you have a #region IMvcRouteExtender. This is a little stickier. I could definitely see a benefit to this if your class was implementing multiple interfaces and you had a region for each interface, but I would remove it for the code here. Clutter like this distracts us from the code.

Some of your instructions also get kind of long. I'd shorten then up a bit with a few line breaks.

/// <summary>
///     Registers the routes.
/// </summary>
/// <param name="routes">The <see cref="RouteCollection" /> to which the routes will be added.</param>
public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    // Get all the types in the references assembly that implement the 'IMvcRouteExtender' interface.
    var types = AppDomain.CurrentDomain.GetAssemblies()
                .ToList()
                .SelectMany(a => a.GetTypes())
                .Where(t => typeof(IMvcRouteExtender).IsAssignableFrom(t) && !t.IsInterface);

    // Register all the routes.
    types.Select(type => Activator.CreateInstance(type) as IMvcRouteExtender)
                .Each(x => x.RegisterRoutes(routes));

    // All the required routes are placed here.
    routes.MapRoute(
            "Website", 
            "{controller}/{action}/{id}", 
            new 
            {
            controller = "Home", 
            action = "Index",
            id = UrlParameter.Optional 
            });
}

It's certainly not ideal still, but I prefer vertical scroll to horizontal scroll. It lets us see what the method is doing "all at once".

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.