Dependency injection in Routes : IRouteHandler

Topics: Writing modules, Writing themes
Feb 13, 2013 at 12:15 PM
I want to set up some route that changes its behavior depending on the current theme. Now, I'm trying to figure out a way to inject an IThemeManager instance into my ThemeFileHandler which inherits from IRouteHandler which I pass into the Route constructor (well, an instance of it, really).

The problem lies in the retrieval of the dependency in the context of the current request.

I'd even get hacky if it helped - already tried implementing IShim and using the OrchardHostContainerRegistry but unfortunately it has access only to the root lifetime scope not the request lifetime scope.

Has anybody done anything like this before or can hint me at a solution?
Developer
Feb 13, 2013 at 2:39 PM
Every IRouteHandler is a singleton that lives in shell context. In order to make it use some component from the work context you need to inject Work<IThemeManager> instead.

Value property of the injected Work<T> object contains the required component resolved from the current work context. Please keep in mind two things though:
  1. Never store the return value of Value directly in a field - call it when you need the underlying object.
  2. Check if Value is not null. It will be if the IRouteHandler will get called from outside work context (ie. from a background thread)
Coordinator
Feb 13, 2013 at 10:40 PM
Having a theme modify the routes sounds like a recipe for failure, in addition to being a confusion of responsibilities.

Please tell us what your scenario is and we'll tell you how to implement it in a cleaner way.
Feb 16, 2013 at 1:55 PM
Our scenario is the following:

We're building a portal software (www.discoverize.com). Without any custom configuration, a new portal has our Neutral theme activated. Once we build a custom theme for a certain portal, we want to diverge from the defaults here and there - e.g. replace default images from the Neutral theme with specialized images for the concrete portal. Everything else should just fall back to using the Neutral theme's resources.

Now, to reference static resources in a clean way I'd just use some unified, theme-agnostic url for them, e.g. /styles/img/favicon.ico instead of /themes/neutral/styles/img/favicon.ico. This is especially important inside CSS files as there I can't modify the generated link the way that would be possible in some razor view, e.g. layout.cshtml.

Just now while writing this I remember that there is the HTML <base> tag but doing a quick search for it pointed me at this stackoverflow question where the OP in his own answer points out the tricky problems this may produce. So I'd be rather unwilling to use the <base> although it'd seem like a perfect fit at first (especially, for the urls embedded in css files).

What other approach would you suggest to satisfy these requirements?
Coordinator
Feb 17, 2013 at 8:11 AM
It's the img tags and stylesheets pointing to those images that should be varied, not the resources themselves. Making the same given URL point to different effective resources depending on context is a very bad idea for the following reasons:
  • it's SEO and HTTP-unfriendly: a given URL is supposed to point always at the same resource. That resource may be dynamic but should be essentially fixed.
  • It's cache-unfriendly: you'll need to vary the cache by the same parameters you use to determine what resource to use.
  • it degrades performance (a lot) by preventing those resources from ever being served directly by IIS.
So what I'd do is to have derived themes, with an implementation of IThemeSelector that decides which one to use. Where you need to vary a resource, you actually vary the template containing the img tag. You can also have the layout template in each theme include a different stylesheet.

But seriously, varying the routes is going to be very difficult to do right, and is an extremely bad idea in the first place. Don't do it.
Feb 21, 2013 at 9:28 AM
Edited Feb 21, 2013 at 9:34 AM
Thanks, Bertrand, for your comment. I had some odd feelings about this solution as well, but wasn't as convinced as you that I should just not do it like that.

I now went with a slightly modified version of the ThemeExtensions.ThemePath() where I check the existence of a given file on disk during rendering and fallback to the version in our Neutral theme if it can't be found. Thus, we always generate correct and unique links for all our static files.

With the stylesheets it's a bit more complicated because they're not rendered during request processing. For now we copy the referenced files over to the inheriting themes, but I'll think about your templating idea once we get there again.