@Shazwazza

Shannon Deminick's blog all about web development

Umbraco supports allowing you to setup and configure any IoC container type that you want to use in your application. For a while now there’s been some sparse documentation on how to achieve this which you can find here: https://our.umbraco.org/Documentation/reference/using-ioc. As the Umbraco core codebase evolves, sometimes a new non-parameterless constructor is added to a class and sometimes this can confuse an existing container that you’ve setup. For many folks, fixing these errors after upgrading is a trial and error experience until they track down the dependency that is now missing from their container and finally add it.

Simone, a very helpful Umbracian, made a comment on the issue tracker and it’s something that is just so obvious  (http://issues.umbraco.org/issue/U4-9562#comment=67-41855):

I think the point here is:  as user of a framework, I shouldn't need to wire up dependencies for internals of the framework myself. I should only bother about my own dependencies.
Maybe Umbraco should ship a small extension method for each of the main IoC container out there which wires up all the internals.
Or come with a IoC container out of the box and then everyone using umbraco have to use that one.

Yes of course this should be done!

A new community project: Our.Umbraco.IoC

I decided to get the ball rolling with this one and have setup a new Git repo here: https://github.com/Shazwazza/Our.Umbraco.IoC 

Currently there are 2 different container configurations committed and working for Autofac and LightInject.

I’ve added some notes to the readme on how to contribute and get started so I’m hoping that some folks can create some Pull Requests to add support for more containers. The project is very easy to navigate, it’s got a build script and nuget packages setup.

Give it a go!

I’ve published some beta’s to Nuget:

Install-Package Our.Umbraco.IoC.Autofac
Install-Package Our.Umbraco.IoC.LightInject

You can actually install both and test each one independently by disabling them by an appSetting:

<add key="Our.Umbraco.IoC.Autofac.Enabled" value="false" />

Or

<add key="Our.Umbraco.IoC.LightInject.Enabled" value="false" />

If this config key doesn’t exist, it will assume the value is “true”

Using the container

Once you’ve got your desired package installed, it will be active in your solution (unless you disable it via config). At this stage you’ll want to add your own bits to the container, so here’s how you do that:

  • Create a custom Umbraco ApplicationEventHandler
  • Override ApplicationInitialized – we do this in this phase to bind to the container event before the container is built which occurs in the ApplicationStarted phase
  • Bind to the container event
  • add any custom services you want to the container

Here’s a full working example showing various techniques and includes the syntax for both LightInject and Autofac. In this example we’re registering a IServerInfoService as a request scoped object since it requires an HttpRequestBase. NOTE: That the basic web objects are already registered in the containers (such as HttpContextBase, HttpRequestBase, etc…)


public class MyUmbracoStartup : ApplicationEventHandler
{
    protected override void ApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
    {
        //If you are using Autofac:
        AutofacStartup.ContainerBuilding += (sender, args) =>
        {
            //add our own services
            args.Builder.RegisterControllers(typeof(TestController).Assembly);
            args.Builder.RegisterType().As().InstancePerRequest();
        };

        //If you are using LightInject:
        LightInjectStartup.ContainerBuilding += (sender, args) =>
        {
            //add our own services
            args.Container.RegisterControllers(typeof(TestController).Assembly);
            args.Container.Register(new PerRequestLifeTime());
        };
    }
}

//service
public interface IServerInfoService
{
    string GetValue();
}

//implementation of the service
public class ServerInfoService : IServerInfoService
{
    private readonly HttpRequestBase _umbCtx;

    //requires a request based object so this must be scoped to a request
    public ServerInfoService(HttpRequestBase umbCtx)
    {
        _umbCtx = umbCtx;
    }

    public string GetValue()
    {
        var sb = new StringBuilder();
        sb.AppendLine("Server info!").AppendLine();
        foreach (var key in _umbCtx.ServerVariables.AllKeys)
        {
            sb.AppendLine($"{key} = {_umbCtx.ServerVariables[key]}");
        }
        return sb.ToString();
    }
}

public class TestController : SurfaceController
{
    private readonly IServerInfoService _serverInfoService;

    public TestController(IServerInfoService serverInfoService, UmbracoContext umbCtx): base(umbCtx)
    {
        _serverInfoService = serverInfoService;
    }

    //see /umbraco/surface/test/index to see the result
    public ActionResult Index()
    {
        return Content(_serverInfoService.GetValue(), "text/plain");
    }
}

Happy holidays!

Paging with Examine

November 3, 2017 10:28

Paging with Lucene and Examine requires some specific API usage. It's very easy to get wrong by using Linq's Skip/Take methods and when doing this you'll inadvertently end up loading in all search results from Lucene and then filtering in memory when what you really want to do is have Lucene only create the minimal search result objects that you are interested in.

There are 2 important parts to this:

  • The Skip method on the ISearchResults object
  • The Search overload on the BaseSearchProvider where you can specify maxResults

ISearchResults.Skip

This is very different from the Linq Skip method so you need to be sure you are using the Skip method on the ISearchResults object. This tells Lucene to skip over a specific number of results without allocating the result objects. If you use Linq’s Skip method on the underlying IEnumerable<SearchResult> of ISearchResults, this will allocate all of the result objects and then filter them in memory which is what you don’t want to do.

Search with maxResults

Lucene isn’t perfect for paging because it doesn’t natively support the Linq equivalent to “Skip/Take”. It understands Skip (as above) but doesn’t understand Take, instead it only knows how to limit the max results so that it doesn’t allocate every result, most of which you would probably not need when paging.

With the combination of ISearchResult.Skip and maxResults, we can tell Lucene to:

  • Skip over a certain number of results without allocating them and tell Lucene
  • only allocate a certain number of results after skipping

Show me the code

//for example purposes, we want to show page #4 (which is pageIndex of 3)
var pageIndex = 3;   
//for this example, the page size is 10 items
var pageSize = 10;
var searchResult = searchProvider.Search(criteria, 
   //don't return more results than we need for the paging
   //this is the 'trick' - we need to load enough search results to fill
   //all pages from 1 to the current page of 4
   maxResults: pageSize*(pageIndex + 1));
//then we use the Skip method to tell Lucene to not allocate search results
//for the first 3 pages
var pagedResults = searchResult.Skip(pageIndex*pageSize);
var totalResults = searchResult.TotalItemCount;

So that is the correct way to do paging with Examine and Lucene which ensures max performance and minimal object allocations.

Isolated WebApi attribute routing

January 17, 2016 12:39

Attribute routing in ASP.Net WebApi is great and makes routing your controllers quite a bit more elegant than writing routes manually. However one problem I have with it is that it is either “on” or “off” at an application level.  There is no way for a library developer to tell ASP.Net to create routes based on attributes for specific controllers or assemblies without forcing the consumer of that library to enable Attribute Routing for the whole application. In many cases this might not matter, but if you are creating a package or library of that contains it’s own API routes, you probably don’t want to interfere with a developers’ normal application setup. There should be no reason why they need to be forced to turn on attribute routing in order for your product to work, and similarly they might not want your routes automatically enabled.

The good news is that this is possible. With a bit of code, you can route your own controllers with attribute routing and be able to turn them on or off without affecting the default application attribute routes. A full implementation of this has been created for the Umbraco RestApi project so I’ll reference that source in this post for the following code examples.

Show me the code

They key to getting this to work is: IDirectRouteProvider, IDirectRouteFactory

The first thing we need is a custom IDirectRouteFactory which is actually a custom attribute. I’ve called this CustomRouteAttribute  but you could call it whatever you want.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class CustomRouteAttribute : Attribute, IDirectRouteFactory

This custom attribute just wraps the default WebApi RouteAttribute’s IDirectRouteFactory implementation so we don’t have to re-write any code for that.

(see full implementation here)

Next we’ll create a custom IDirectRouteProvider:

/// <summary>
/// This is used to lookup our CustomRouteAttribute instead of the normal RouteAttribute so that 
/// we can use the CustomRouteAttribute instead of the RouteAttribute on our controlles so the normal
/// MapHttpAttributeRoutes method doesn't try to route our controllers - since the point of this is
/// to be able to map our controller routes with attribute routing explicitly without interfering
/// with default application routes.
/// </summary>
public class CustomRouteAttributeDirectRouteProvider : DefaultDirectRouteProvider
{
    private readonly bool _inherit;

    public CustomRouteAttributeDirectRouteProvider(bool inherit = false)
    {
        _inherit = inherit;
    }

    protected override IReadOnlyList<IDirectRouteFactory> GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
    {
        return actionDescriptor.GetCustomAttributes<CustomRouteAttribute>(inherit: _inherit);
    }
}

So far this is all pretty straight forward so far but here’s where things start to get interesting. Because we only want to create routes for specific controllers, we need to use a custom IHttpControllerTypeResolver. However, since the HttpConfiguration instance only contains a single reference to the IHttpControllerTypeResolver we need to do some hacking. The route creation process for attribute routing happens during the HttpConfiguration initialization so we need to create an isolated instance of HttpConfiguration, set it up with the services we want to use, initialize it to create our custom routes and assign those custom routes back to the main application’s HttpConfiguration.

Up first, we create a custom IHttpControllerTypeResolver to only resolve the controller we’re looking for:

public class SpecificControllerTypeResolver : IHttpControllerTypeResolver
{
    private readonly IEnumerable<Type> _controllerTypes;

    public SpecificControllerTypeResolver(IEnumerable<Type> controllerTypes)
    {
        if (controllerTypes == null) throw new ArgumentNullException("controllerTypes");
        _controllerTypes = controllerTypes;
    }

    public ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver)
    {
        return _controllerTypes.ToList();
    }
}

Before we look at initializing a separate instance of HttpConfiguration, lets look at the code you’d use to enable all of this in your startup code:

//config = the main application HttpConfiguration instance
config.MapControllerAttributeRoutes(
    routeNamePrefix: "MyRoutes-",
    //Map these explicit controllers in the order they appear
    controllerTypes: new[]
    {                    
        typeof (MyProductController),
        typeof (MyStoreController)
    });

The above code will enable custom attribute routing for the 2 specific controllers. These controllers will be routed with attribute routing but instead of using the standard [Route] attribute, you’d use our custom [CustomRoute] attribute. The MapControllerAttributeRoutes extension method is where all of the magic happens, here’s what it does:

  • Iterates over each controller type
  • Creates an instance of HttpConfiguration
  • Sets it’s IHttpControllerTypeResolver instance to SpecificControllerTypeResolver for the current controller iteration (The reason an instance of HttpConfiguration is created for each controller is to ensure that the routes are created in the order of which they are specified in the above code snippet)
  • Initialize the HttpConfiguration instance to create the custom attribute routes
  • Copy these routes back to the main application’s HttpConfguration route table

You can see the full implementation of this extension method here which includes code comments and more details on what it’s doing.  The actual implementation of this method also allows for some additional parameters and callbacks so that each of these routes could be customized if required when they are created.

 

There is obviously a bit of code involved to achieve this and there could very well be a simpler way, however this implementation does work rather well and offers quite a lot of flexibility. I’d certainly be interested to hear if other developers have figured this out and what their solutions were.

logo-powershell-umbracoSince I like using Powershell for my build scripts for various projects I thought it would be handy to create a Powershell script to create an Umbraco package in it’s native package format. I’ve added this script template to my GitHub Umbraco Scripts repository which you can see here: http://bit.ly/1kM9g9g

I’ve tried to add a bit of documentation to this script and in theory you should only need to set up the paths properly. You’ll also need to ensure that you initially create the package in the back office of Umbraco in order to generate the createdPackages.config as mentioned in the script notes.

I use this script in Articulate’s build script so at least I can also provide an example of using it which you can see here: https://github.com/Shazwazza/Articulate/blob/master/build/build.ps1

There’s a chance you might not have heard of FCN (File Change Notification) in ASP.Net and there’s an even bigger chance you didn’t realize how much it might affect you.

What is FCN in ASP.Net?

As you know ASP.Net monitors a few files/folders such as the ~/web.config and ~/App_Code and will restart your app domain when it detects changes. This is part of FCN in ASP.Net but it goes much deeper than that. There are a few other files & folders that ASP.Net monitors which will also cause an app domain restart: bin, App_WebReferences, App_GlobalResources, App_Code, Global.asax, and others. However what you might not realize is that ASP.Net actually monitors every single folder (+ files) in your web app!

Update 29/07/2016! I found a nice MS article about FCN here. I’ve been looking for more resources about this since a few new issues have been cropping up and I’m wondering if another MS update has caused another problem. Recently we’ve seen an increase in the error: “Overwhelming Change Notification in …” which has everything to do with FCN. I’ve also added a few links to the bottom of this post.

Why do I care?

If you have a web app that contains a lot of folders there is a good chance that the performance of your file system is affected in one way or another. To give you an example of how many file system watchers ASP.Net generates I created a Razor view (.cshtml)  (which you should definitely test on your own site!) to display what is actually happening behind the scenes. Here’s the output for the first run page from the Visual Studio 2015 MVC template site:

image

The table above lists all of the “DirectoryMonitor” instances and the folder they are attached to along with all of the “FileMonitor” instances attached to that DirectoryMonitor. It’s worth nothing that these are not simply just .Net’s FileSystemWatcher, but some sort of native windows IO using a delegate called NativeFileChangeNotification. The above table doesn’t seem too scary but these monitors are not static either, they grow with every directory and file that is accessed in your web app.  For example, if I navigate to these pages: /Home/About, /Home/Contact, /Account/Register, /Account/Login and go back to view this table it looks like:

image

Things start to get interesting when you have a web application that has a lot of folders. Some examples of this might be:

  • You are using a dynamic image processor such as Image Resizer or Image Processor since these will create a lot of folders based on hashes to store these dynamic images
  • Maybe you have a lot of members/users on your site and you have one or more folders for each one
  • Maybe you use nodejs or bower and you store these generated folders in your web root and references the assets directly in those folders … there can be tons of folders in bower_components and node_modules
  • You could be using a web framework or CMS like Umbraco or Orchard (I’m sure there are plenty of others) that contain quite a lot of folders by default

Here’s the truncated result of an Orchard site after visiting the admin area, creating a page and displaying it:

image

Whoa!

ASP.Net’s FCNMode

At some stage in ASP.Net’s lifetime somebody must have complained about this to Microsoft and they released a hotfix (seen here: https://support.microsoft.com/en-us/kb/911272). Then I can only assume that other people complained about this and with .Net 4.5 a new configuration setting appeared: FCNMode. This documentation explains a little about what each option does:

  • Default - For each subdirectory, the application creates an object that monitors the subdirectory. This is the default behavior
  • Disabled - File change notification is disabled.
  • NotSet - File change notification is not set, so the application creates an object that monitors each subdirectory. This is the default behavior.
  • Single - The application creates one object to monitor the main directory and uses this object to monitor each subdirectory.

Unfortunately these docs don’t tell us the whole story. It’s obvious that Default/NotSet are the same thing and are the default. Disabled is fairly clear but what it doesn’t mention is that if you set it to Disabled, this will disable all FCN for your web app, so if you change the contents of /bin or /App_Code, the site will not restart. However, Disabled still means that the web.config file is monitored so if you use this setting you can still bump your web.config to restart your site.

What exactly is “Single” though?

The folks over at DNN seem to have quite a bit of experience with FCN and this article seems to be the only place that actually explains “Single” mode correctly:

“FCNMode creates a monitor object with a buffer size of 4KB for each folder. When FCNMode is set to Single, a single monitor object is created with a buffer size of 64KB. When there are file changes, the buffer is filled with file change information. If the buffer gets overwhelmed with too many file change notifications an “Overwhelming File Change Notifications” error will occur and the app domain will recycle. The likelihood of the buffer getting overwhelmed is higher in an environment where you are using separate file server because the folder paths are much larger.”

If I change fcnMode=”Single” in my web.config for the same Orchard site above, the results are:

image

That’s quite a bit less!

I’m sure there are pros to using “Default” instead of “Single” but I’m not actually sure what they are.

Real world problem

The circumstance where “Default” FCN mode becomes a real problem is when you have a website that is running off of a remote file share.  As noted in Shawn Walker’s DNN article mentioned above:

“The likelihood of the buffer getting overwhelmed is higher in an environment where you are using separate file server because the folder paths are much larger.”

I can’t actually see a performance difference between Default or Single when running locally with an SSD HD, but I have seen some big issues running with Default when hosting on a remote file server:

  • Constant app domain restarts – I’ve seen constant app domain restarts even when a site is just serving requests without any IO activity apart from just reading. And by ‘Constant’ … I mean every few seconds all day long.
  • File server performance suffers severely – When multiple sites are active and are being hosted on a remote file server with Default FCNMode, the writing performance of the file server is drastically degraded even though when monitoring IOPS there doesn’t appear to be any issues

To solve this issue we changed fcnMode=”Single” in the machine.config so that all sites would effectively use “Single”… and the result was instant: No more constant app restarts, file server performance was instantly back to normal. And as far as I can tell, there has been no downside to running FCNMode in Single.

So I really wonder what the up-side of Default is when it seems that running in Single mode is perfectly stable running on any hosting environment… ?

As far as I can tell, this won’t be an issue with aspnet5 … let’s just hope!

More info?

Here’s a list of helpful links about FCN in ASP.Net:

Updated 29/07/2016! – More links I’ve discovered. Turns out this has been an issue for IIS + ASP.Net for quite some time with various older hotfixes, some of these new links might shed some light on the particular problem you might be having.

Using LinqPad with Umbraco

March 27, 2015 08:07

During some spare time last weekend I decided to look into booting up Umbraco outside of a web context. I know this has been done before in various different ways but for this I wanted to just try to use the Umbraco core. There are 2 things that are required to boot Umbraco:

  • IBootManager
  • UmbracoApplicationBase

In Umbraco source, there are a couple of these instances: CoreBootManager, WebBootManager & UmbracoApplication.  Essentially the WebBootManager starts up anything that has web requirements and inherits from CoreBootManager which starts up the rest of Umbraco and all of that is bootstrapped with UmbracoApplication, which is actually the instance of the global.asax in an Umbraco website. To boot just the core of Umbraco it would seem clear that I just want CoreBootManager and then bootstrap it myself with a custom UmbracoApplicationBase. All of this is actually very simple and the bare bones would look something like:

public class ConsoleApplication : UmbracoApplicationBase
{  
    public void StartApplication()
    {
        GetBootManager()
            .Initialize()
            .Startup(appContext => OnApplicationStarting(this, new EventArgs()))
            .Complete(appContext => OnApplicationStarted(this, new EventArgs()));
    }

    protected override IBootManager GetBootManager()
    {
        return new ConsoleBootManager();
    }
}

Then to use it could look something like:

public void Main()
{
    using (var app = new ConsoleApplication())
    {
        app.StartApplication();
        using (var appCtx = ApplicationContext.Current)
        {
            //do stuff
        }
    }
}

Let’s just say there are still some issues with this that we’ll iron out in the future. Almost all of the issues have to do with loading in plugins and plugins not being capable of running without web context, and various other things. The good news is that I got it working with only a tiny bit of reflection which basically just removes all IApplicationEventHandlers from starting up apart from the ones found in the Umbraco Core assembly.

LinqPad & Umbraco is real!

Knowing that I could boot Umbraco and interact with it’s data via the ApplicationContext, I figured the next best thing would be to see if I could get that working with LinqPad… and I did !  Awesome!! :)

So here you go, a shiny new v1.0 LinqPad driver for Umbraco and all of the source code:

Be sure to read the docs, there’s probably a lot of stuff it can’t do, but there’s certainly a lot of stuff it could do!!! The next version will hopefully have some proper IQueryable support which I’ve been working on as well.

See it in action:

Umbraco LinqPad driver

Articulate 1.0.4 released

October 10, 2014 06:18

I’ve finally got around to releasing Articulate 1.0.4 today. Want to say a big thanks to all those who submitted pull requests, you guys rock! There’s a few nice fixes in this release but most importantly it fixes the issues with multi-tenancy when domains are assigned in Umbraco.

Here’s the release notes on GitHub with links to each issue fixed:

https://github.com/Shandem/Articulate/releases/tag/1.0.4

You can download the Umbraco package from there or from Our here:

http://our.umbraco.org/projects/starter-kits/articulate

Unfortunately I haven’t got around to getting this on Nuget yet – That’s because it has it’s own challenges since I’d like to perform a full Umbraco data install via Nuget… coming soon I hope :)

Enjoy!

ASP.Net Identity for Umbraco

August 7, 2014 01:31

I’ve released version 1.0 of UmbracoIdentity which allows for ASP.Net Identity to be used with Umbraco for front-end members. I’ve tried to write enough documentation for you to get started, all of the code and docs are here: https://github.com/Shandem/UmbracoIdentity

It’s worth noting that this is not something that ‘everyone’ should just jump in with and start using. If you are not familiar with OWIN or ASP.Net Identity than none of this will really make any sense, I’ve added a bit of a disclaimer to the docs about this here: https://github.com/Shandem/UmbracoIdentity#owin-setup

There are some known issues and limitations that you should be aware of, and this will also not work currently with back office users (that will come eventually too though).

This package can be highly customized, it comes with many .cshtml views and c# classes that you can customize as you see fit – very similar to the Visual Studio 2013 Web Application templates that support ASP.Net Identity.

What about Asp.Net Membership with Umbraco?

The way that I’ve created this is to be 100% compatible with the current Membership structure in Umbraco. This means that this is not using EntityFramework to access data, it uses the Umbraco member services and providers using custom ASP.Net Identity user stores. This also means that the password formats are based on the current password formats of the membership provider. When the Nuget package is installed it will actually swap out the membership provider for a custom type: UmbracoIdentity.IdentityEnabledMembersMembershipProvider. This is required so that the password security can still be handled by the membership provider logic.

There is a note about passwords here, ASP.Net Identity normally will format and salt passwords different (slightly better) than how the membership providers current work, but if you need that functionality you’d have to implement your own IPasswordHasher.

Installation

It is absolutely essential you read the documentation before installing. Once you’ve done that, you can use Nuget:

PM> Install-Package UmbracoIdentity

I’ve recently decided to build a new open source blog engine powered by Umbraco called Articulate. There’s a few reasons why i wanted to do this:

  • Since my full time job is an Umbraco core developer, I spend most of my time building Umbraco and get very little time for using Umbraco. I wanted to change this dilemma and really use Umbraco and utilize many of the features we’ve been creating and try to push some of it’s boundaries. For me this is a perfect way to find inspiration for new Umbraco features and enhancements.
  • I’ve wanted to move my blog away from BlogEngine.Net to Umbraco for quite some time (dogfooding) but I really needed to have every feature that I use in BlogEngine.Net available
  • I wanted to make the blog experience as simple as possible in Umbraco
  • I wanted to be able to write blog posts directly from my mobile phone easily using the web

I’ve got some documentation written and I’ll keep updating this over time but hopefully there’s enough there to get you started. Since Articulate is open source and hosted on GitHub, any community contributions are hugely welcomed :) Whether that’s core code additions, fixes, documentation, etc… any contribution is a big help.

Articulate templates are based on themes and I think it would be super awesome if people started creating their own themes and releasing them as their own packages or including them in the Articulate core for release. Creating themes is really easy and in fact a few of the themes included with Articulate are open source MIT licensed themes migrated over from the Ghost blogging platform (which is also very easy to do).

The first version of Articulate is out and can be downloaded either from the Umbraco package repository or from GitHub. There are a few minor bugs in this release that have been reported and fixed and I’ll have a newer version out this week.

A while ago I wrote a post on how to do custom MVC routing in Umbraco, though the end result wasn’t quite ideal. There were a few tricks required and It wasn’t perfect since there were problems with rendering macros on the resulting view, etc… This was due to not having a PublishedContentRequest object assigned to the context. So then we went ahead and created a new attribute to assign to your MVC action to resolve this: [EnsurePublishedContentRequestAttribute]

Like the last post, you can read a lot about all of this in this Our thread. With the [EnsurePublishedContentRequestAttribute] attribute you could now assign any IPublishedContent instance to a PublishedContentRequest and be sure that it was assigned to the UmbracoContext. But this still isn’t the most ideal way to go about specifying MVC routes to work within the Umbraco pipeline… so I’ve created the following implementation which works quite well.

Background

A little bit of background in to custom MVC routes and Umbraco… The reason why it is not terribly straight forward to create a custom route and have it assigned to an Umbraco node is because the node doesn’t exist at your custom route’s location.

For example, if we have this route:

//Create a custom route
RouteTable.Routes.MapRoute(
    "test",
    "Products/{action}/{sku}",
    new
        {
            controller = "MyProduct", 
            action = "Product", 
            sku = UrlParameter.Optional
        });

Umbraco by default would have no idea what node (IPublishedContent) would be assigned to this. The way Umbraco relates a URL to an IPublishedContent instance is by a list of IContentFinder’s. A very easy way to relate a custom URL to an IPublishedContent instance is to create your own IContentFinder. Combine that with route hijacking and in many cases this would probably be enough for your custom routing needs. However, it does not solve how you would wire up custom route parameters to your controller like how MVC normally works. Like in the above routing example, you’d want to have the ‘sku’ parameter value wired up to your Action parameter.

The above route can work and be integrated into Umbraco by following some aspects of my previous blog post and use the [EnsurePublishedContentRequestAttribute], but we can make it easier…

Creating routes

The simplest way to demonstrate this new way to create MVC routes in Umbraco is to just show you an example, so here it is:

//custom route
routes.MapUmbracoRoute(
    "test",
    "Products/{action}/{sku}",
    new
    {
        controller = "MyProduct",
        sku = UrlParameter.Optional
    },
    new ProductsRouteHandler(_productsNodeId));

This is using a new extension method: MapUmbracoRoute which takes in the normal routing parameters (you can also include constraints, namespaces, etc….) but also takes in an instance of UmbracoVirtualNodeRouteHandler.

The instance of UmbracoVirtualNodeRouteHandler is responsible for associating an IPublishedContent with this route. It has one abstract method which must be implemented:

IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext)

It has another virtual method that can be overridden which will allow you to manipulate the PublishedContentRequest however you’d like:

PreparePublishedContentRequest(PublishedContentRequest publishedContentRequest)

So how do you find content to associate with the route? Well that’s up to you, one way (as seen above) would be to specify a node Id. In the example my ProductsRouteHandler is inheriting from UmbracoVirtualNodeByIdRouteHandler which has an abstract method:

IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext, 
    IPublishedContent baseContent);

So based on all this information provided in these methods, you can associate whatever IPublishedContent item you want to the request.

Virtual content

This implementation expects any instance of IPublishedContent, so this means you can create your own virtual nodes with any custom properties you want. Generally speaking you’ll probably have a real Umbraco IPublishedContent instance as a reference point, so you could create your own virtual IPublishedContent item based on PublishedContentWrapped, pass in this real node and then just override whatever properties you want, like the page Name, etc..

Whatever instance of IPublishedContent returned here will be converted to a RenderModel for use in your controllers.

Controllers

Controllers are straight forward and work like any other routed controller except that the Action will have an instance of RenderModel mapped to it’s parameter. Example:

public class MyProductController : RenderMvcController
{
    public ActionResult Product(RenderModel model, string sku)
    {
        //in my case, the IPublishedContent attached to this
        // model will be my products node in Umbraco which i 
        // can now use to traverse to display the product list
        // or lookup the product by sku
            
        if (string.IsNullOrEmpty(sku))
        {
            //render the products list if no sku
            return RenderProductsList(model);
        }
        else
        {
            return RenderProduct(model, sku);
        }
    }
}

I have this all working well in a side project of mine at the moment. This functionality will be exposed in an upcoming Umbraco version near you  :)

It’s also worth noting that all of this was accomplished outside of the Umbraco core with the publicly available APIs that currently exist. I will admit though there were a few hacks involved which of course won’t be hacks when moved into the core ;)