Shazwazza

Shannon Deminick's blog all about .Net, Umbraco & Web development

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

ClientDependency 1.8 released

July 7, 2014 08:02

It’s taken me forever to get this release out purely due to not having enough time, but here it finally is. This update now multi-targets framework versions:

  • Core project now targets both .Net 4 and 4.5
  • MVC project is now targets both .Net 4 and 4.5 for MVC 4 and .Net 4.5 for MVC 5

There are also a couple of minor bug fixes:

The update to the CDF .Less project is the update to use the latest .Less 1.4.0.0 version.

To install the CDF core:

PM> Install-Package ClientDependency

To install CDF for MVC (less than v5):

PM> Install-Package ClientDependency-MVC

If you are using MVC 5 then you’ll need to use the MVC 5 specific version:

PM> Install-Package ClientDependency-MVC5

To install the .Less update:

PM> Install-Package ClientDependency-Less

Remember CDF also supports TypeScript, CoffeeScript and SASS!

PM> Install-Package ClientDependency-TypeScript

PM> Install-Package ClientDependency-CoffeeScript

PM> Install-Package ClientDependency-SASS

Multiple WebApi controllers with the same name but different namespaces

June 27, 2014 05:56

Warren recently reported this issue on Umbraco which prohibits WebApi from routing to two different paths that specify the same controller name but different namespaces. This type of thing is fully supported in MVC but not in WebApi for some reason.

Here’s a quick example, suppose we have two controllers:

namespace Test1
{
    [PluginController("Test1")]
    [IsBackOffice]
    public class ConfigController : UmbracoApiController
    {
        public int GetStuff()
        {
            return 9876;
        }
    }
}
namespace Test2
{
    [PluginController("Test2")]
    [IsBackOffice]    
    public class ConfigController : UmbracoApiController
    {
        public int GetStuff()
        {
            return 1234;
        }
    }
}

These controller definitions will create routes to the following paths respectively:

  • /umbraco/backoffice/test1/config/getstuff
  • /umbraco/backoffice/test2/config/getstuff

When these routes are created, the “Namespaces” data token is specified on the route, just like what is done in MVC, however in WebApi that needs to be done manually. Example:

var r = routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);
r.DataTokens["Namespaces"] = new string[] {"Foo"};

but if you navigate to either of these paths you’ll end up with a message like:

Multiple types were found that match the controller named 'Config'. This can happen if the route that services this request ('umbraco/backoffice/Test2/Config/{action}/{id}') found multiple controllers defined with the same name but differing namespaces, which is not supported. The request for 'Config' has found the following matching controllers: Test1.ConfigController Test2.ConfigController

Custom IHttpControllerSelector

To achieve what we want, we need to create a custom IHttpControllerSelector. I’ve created this in the Umbraco core to solve the issue and the source can be found HERE. The implementation is pretty straight forward – it relies on the default WebApi controller selector for everything unless a “Namespaces” data token is detected in the route and more than one controller type was found for the current controller name in the app domain.

There’s some posts out there that elude to the possibility of this being supported in WebApi in the future but as of the latest source code for the DefaultHttpControllerSelector, it appears that the functionality is not yet there.

If you need this functionality though, this implementation is working and pretty simple. To register this selector just use this code on startup:

GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector),
    new NamespaceHttpControllerSelector(GlobalConfiguration.Configuration));

This blog now powered by Articulate

June 26, 2014 04:14

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.

Custom MVC routes within the Umbraco pipeline

May 24, 2014 02:03

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 ;)

Multi targeting a single .Net project to build for different framework versions

May 2, 2014 04:18

Consider this scenario:

  • I have a project that relies on ASP.Net MVC and currently this project is built against the .Net framework 4.0 and MVC 4
  • I want to support MVC 5 for this project
  • To support MVC 5, I’d need to upgrade the project to use .Net framework 4.5
  • This would mean that developers still running .Net framework 4.0 would no longer be able to use my updated project

I also don’t want to have to create different projects and assemblies that target specific MVC versions (in this case you’d end up with assembly names like MyProject.MVC4.dll and MyProject.MVC5.dll). I have seen this done with other solutions and it works but I feel it is not necessary unless your project is using specific functionality from a particular MVC version.

In my case my project will compile perfectly against MVC 4 and 5 without any codebase changes so I’m not actually targeting against a specific MVC version ( >= 4 ). If your project uses specific functionality from the different MVC versions I think you will have to output different assemblies like MyProject.MVC5.dll. In that case this post will probably help: http://blogs.korzh.com/2013/12/nuget-package-different-mvc-versions.html

The goal

The end result is that I want a single project file that has 4 build configurations:

  • Debug / Release
    • These will build against .Net 4.0
    • This will reference MVC 4
  • Debug-Net45 / Release-Net45
    • This will build against .Net 4.5
    • This will reference MVC 5

Each of these build configurations exports the same assembly name: MyProject.dll

I then want a single NuGet package which will install the correct DLL based on the .Net version that the user has installed.

How it’s done

Build configuration setup

To start with you’ll have a project that targets .Net framework 4.0 and references MVC 4 and 2 standard build configurations: Debug, Release.

First we need to create 2 new build configurations: Debug-Net45 and Release-Net45 and have these configurations output the DLLs to a custom folder. To do this we launch the Configuration Manager:

2014-04-30_2014

Create a new build configuration:

2014-04-30_2016

Enter the name of the new configuration and copy the configuration from it’s associated existing one. So Debug-Net45 would copy from Debug and Release-Net45 copies from Release.

2014-04-30_2021

Do this for both configurations – Debug-Net45 and Release-Net45.

Project setup

The next step requires manually editing your .csproj file since Visual Studio doesn’t want to normally allow you to do this. This is how you can configure each build type to target a different framework version. You’ll first need to find these 2 entries:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">

Inside each of these elements you need to explicitly tell these build configurations to use .Net framework 4.0 by adding this element:

<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>

Next you’ll want to copy/paste both of these property groups and:

  • Change the Condition to check for the new build configurations and to target .Net framework 4.5
  • Change the OutputPath to have a specific .Net 4.5 folder
  • Change the TargetFrameworkVersion to be .Net 4.5

You should end up with 2 new elements that look something like:

<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug-Net45|AnyCPU'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\Debug-Net45\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>AnyCPU</PlatformTarget>
    <ErrorReport>prompt</ErrorReport>
    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
    <PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release-Net45|AnyCPU'">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release-Net45\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
    <PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>

Next we need to do this for the MVC references or any framework specific references you need to target. In this example it is just the MVC references and you’ll need to wrap them with an ItemGroup element, you’ll end up with something like this to target the MVC 4 libs:

<ItemGroup Condition=" '$(Configuration)'=='Debug' Or '$(Configuration)'=='Release'">
    <Reference Include="System.Web.Helpers, Version=2.0.0.0, .....">
      <Private>True</Private>
      <HintPath>
        ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll
      </HintPath>
    </Reference>
    <Reference Include="System.Web.Mvc, Version=4.0.0.0, .....">
      <Private>True</Private>
      <HintPath>
        ..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll
      </HintPath>
    </Reference>
    <Reference Include="System.Web.Razor, Version=2.0.0.0, .....">
      <Private>True</Private>
      <HintPath>
        ..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll
      </HintPath>
    </Reference>
    <Reference Include="System.Web.WebPages, Version=2.0.0.0, .....">
      <Private>True</Private>
      <HintPath>
        ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll
      </HintPath>
    </Reference>
    <Reference Include="System.Web.WebPages.Deployment, Version=2.0.0.0, .....">
      <Private>True</Private>
      <HintPath>
        ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll
      </HintPath>
    </Reference>
    <Reference Include="System.Web.WebPages.Razor, Version=2.0.0.0, .....">
      <Private>True</Private>
      <HintPath>
        ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Razor.dll
      </HintPath>
    </Reference>
</ItemGroup>

Then we need to go ahead and copy/paste this block but target the different build configurations and then swap out these MVC 4 version references with the MVC 5 version references.

Nuget package restore

For this tutorial I’m assuming you are using Nuget to reference your MVC libs and an easy way to get Nuget to play reasonably with this setup is to enable Nuget package restore for your solution – right click on your solution and click the button:

2014-05-01_1625

Then in your packages.config file you can manually add the MVC 5 references:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Microsoft.AspNet.Mvc" 
           version="4.0.30506.0" targetFramework="net40" />
  <package id="Microsoft.AspNet.Razor" 
           version="2.0.30506.0" targetFramework="net40" />
  <package id="Microsoft.AspNet.WebPages" 
           version="2.0.30506.0" targetFramework="net40" />
  <package id="Microsoft.Web.Infrastructure" 
           version="1.0.0.0" targetFramework="net40" />
  
  <package id="Microsoft.AspNet.Mvc" 
           version="5.0.0" targetFramework="net45" />
  <package id="Microsoft.AspNet.Razor" 
           version="3.0.0" targetFramework="net45" />
  <package id="Microsoft.AspNet.WebPages" 
           version="3.0.0" targetFramework="net45" />
</packages>

Now when you build your solution under the different build configurations Nuget will automatically go and get the correct MVC versions based on the current .Net Framework version.

Thats it!

With all of this in place it should ‘just work’ :) You will notice some odd looking icons in your references list in Visual Studio but it’s nothing to worry about, Visual Studio is just confused because it isn’t familiar with how you’ve tricked it!

2014-05-01_1709

So now when you build using your different build configurations, it will output the correct DLLs to the correct build folders:

  • Both Debug & Release will output to /bin/Debug & bin/Release with MVC 4 libraries and the MyProject.dll will be compiled against .Net Framework 4.0
  • Both Debug-Net45 & Release-Net45 will be output to /bin/Debug-Net45 & /bin/Release-Net45 with MVC 5 libraries and the MyProject.dll will be compiled against .Net Framework 4.5

And here’s some proof:

2014-05-01_1714

2014-05-01_1715

If you are using some automated build processes with MSBuild you can then just target the build configuration names you’d like to export.

The Nuget setup is pretty simple since you can target your dependencies by framework:

<group targetFramework="net40">
<!--between 4 and less than version 5-->
<dependency id="Microsoft.AspNet.Mvc" version="(4.0.20710,5)" />
</group>

<group targetFramework="net45">
<!--between 4 and less than version 6 (who knows what'll happen then)-->
<dependency id="Microsoft.AspNet.Mvc" version="(4.0.20710,6)" />
</group>

Then for each of your lib files in Nuget you ensure that you output them to the framework specific /lib folders such as:

target="lib\net40"
target="lib\net45"

This process has been implemented for the Client Dependency Framework version 1.8.0 to support MVC 4 and 5 without having to change the assembly name.

WebApi per controller configuration

March 21, 2014 00:56

This is more of a blog post about what not to do :)

At first glance, it would seem relatively painless to change your WebApi controller’s configuration, I’d assume most people would do what I initially did. Say for example you wanted to have your controller only support JSON, here’s what I initially tried (DO NOT DO THIS):

protected override void Initialize(HttpControllerContext controllerContext)
{
    base.Initialize(controllerContext);
    var toRemove = controllerContext.Configuration.Formatters
        .Where(t => (t is JsonMediaTypeFormatter) == false).ToList();
    foreach (var r in toRemove)
    {
        controllerContext.Configuration.Formatters.Remove(r);
    }
}

Simple right, just override initialize in your controller and change the current controllerContext’s configuration…. WRONG :(

What this is actually doing is modifying the global WebApi configuration though it’s not clear that this is the case. Unfortunately the actual Configuration property on the controllerContext instance is assigned to the global one. I’m assuming the WebApi team has done this for a reason but I’m not sure what that is; as seen above it’s very easy to change the global WebApi configuration at runtime. Seems to me like it might have been a better idea to clone the global configuration instance and assign that to each HttpControllerContext object.

The correct way to specify per controller custom configuration in WebApi is to use the IControllerConfiguration interface. You can read all about here and it is fairly simple but it does seem like you have to jump through a few hoops for something that initially seems very straight forward.

Get JQuery requests to play nicely with AngularJS CSRF convention

December 6, 2013 19:06

Decided to write this quick post for anyone searching on this topic. AngularJS has it’s own convention for CSRF (Cross Site Request Forgery) protection but in some cases you’ll be calling these same server side services via JQuery so you might need to get JQuery requests to also follow Angular’s convention.

For information about Angular’s CSRF protection see the “Security Considerations” part of Angular’s $http documentation.

Luckily it’s pretty darn easy to get JQuery to follow this convention too and this will also work with 3rd party plugins that use JQuery for requests like Blueimp file uploader. The easiest way to get this done is to set the global JQuery $.ajax rules. Probably the best place to do this is in your Angular app.run statement:

app.run(function ($cookies) {

    //This sets the default jquery ajax headers to include our csrf token, we
    // need to user the beforeSend method because the token might change 
    // (different for each logged in user)
    $.ajaxSetup({
        beforeSend: function (xhr) {
            xhr.setRequestHeader("X-XSRF-TOKEN", $cookies["XSRF-TOKEN"]);
        }
    }); 
});

That’s it!

It’s important to note to set the header using beforeSend, if you just set $.ajax options ‘headers’ section directly that means the header cannot be dynamic – which you’ll probably want if you have users logging in/out.

How to inspect assemblies with reflection before including them in your application

October 25, 2013 22:13

If you application supports plugins or extensions in some cases it might be useful to scan a packages assemblies before importing them in to your app. Some reasons for this might be:

  • Checking if the package has missing assembly references
  • Checking if the assembly references obsolete types that might make the package unstable
  • Checking the .Net targeted framework of the assembly
  • Any other assembly inspection to determine it is compatible with your app

To do this you can load assemblies using Assembly.ReflectionOnlyLoadFrom and Assembly.ReflectionOnlyLoad methods which load assemblies into a special assembly load context called “reflection-only context” which will safely let you inspect these assemblies.

Further reading:

A great article on Reflection Only Assembly Loading and if you want to know more about assembly load contexts, here’s an explanation.

Loading in assemblies

For this example, we’ll assume that all of the assemblies for a package are in some folder outside of the normal /bin folder (not loaded in to the current app) and each assembly for the package will need to be inspected for type references that are not supported.

A common mistake when loading in assemblies with reflection (especially in the LoadFrom context) is to load them in one at a time, whereas they generally will need to be all loaded in before inspecting them since they probably have references to each other. Another thing that generally must be done is adding an event listener on AppDomain.ReflectionOnlyAssemblyResolve because even though all known referenced assemblies are loading into the context some assemblies might not be explicitly referenced but are need to load the assembly. This handler provides a way to resolve those missing references.

The first thing to do is setup the event handler

AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) =>
{
    var a = Assembly.ReflectionOnlyLoad(e.Name);
    if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name);
    return a;
};

Next we need to load all of the assembly files in the folder

foreach (var f in files) Assembly.ReflectionOnlyLoadFrom(f);

Then load all of their referenced assemblies in to the reflection context

//Then load each referenced assembly into the context
var assembliesWithErrors = new List<string>();
foreach (var f in files)
{
    var reflectedAssembly = Assembly.ReflectionOnlyLoadFrom(f);
    foreach (var assemblyName in reflectedAssembly.GetReferencedAssemblies())
    {
        try
        {
            Assembly.ReflectionOnlyLoad(assemblyName.FullName);
        }
        catch (FileNotFoundException)
        {
            //if an exception occurs it means that a referenced assembly could not be found                        
            errors.Add(
                string.Concat("This package references the assembly '",
                    assemblyName.Name,
                    "' which was not found, this package may have problems running"));
            assembliesWithErrors.Add(f);
        }
    }
}

In the catch, I’m detecting assembly reference errors and adding an error message to the outgoing method response and also adding that assembly to the assembliesWithErrors list which is used later to ensure we’re not inspecting assemblies that couldn’t be loaded.

Now that all the assemblies are loaded we can inspect them (ignoring ones with errors). This example is looking for any assemblies that have types implementing ‘MyType’. If they do implement this type, add the assembly to a list to return from the current method.

//now that we have all referenced types into the context we can look up stuff
foreach (var f in files.Except(assembliesWithErrors))
{
    //now we need to see if they contain any type 'MyType'
    var reflectedAssembly = Assembly.ReflectionOnlyLoadFrom(f);
    var found = reflectedAssembly.GetExportedTypes()
        .Where(TypeHelper.IsTypeAssignableFrom<MyType>);
    if (found.Any())
    {
        dllsWithReference.Add(reflectedAssembly.FullName);
    }
}

Separate AppDomain

It’s best to execute all of this logic in a separate AppDomain because once assemblies are loaded in to a context, they cannot be unloaded and since we are loading in from files, those files will remain locked until the AppDomain is shutdown. Explaining how to create a separate AppDomain is outside the scope of this article but the code is included in the source below.

Source Code

Here’s a class the encapsulates all of this logic and of course if you can do much more when inspecting assemblies for various types.

https://gist.github.com/Shandem/7147978

Custom MVC routing in Umbraco

July 4, 2013 23:00

This post will describe how you can declare your own custom MVC routes in order to execute your own custom controllers in Umbraco but still be able to render Umbraco views with the same model that Umbraco uses natively.

NOTE: This post is not about trying to execute a particular Umbraco page under a custom URL, that functionality can be accomplished by creating a custom IContentFinder (in v6.1), or by applying the umbracoUrlAlias

There’s a long (but very useful) thread on Our describing various needs for custom MVC routing inside of Umbraco, definitely worth a read. Here I’ll try to describe a pretty easy way to accomplish this. I’m using Umbraco v6.0.7 (but I’m pretty sure this will work in v4.10+ as well).

Create the route

This example will use an IApplicationEventHandler (in 6.1 you should use the base class ApplicationEventHandler instead). Here I’m defining a custom route for handling products on my website. The example URLs that I want handled will be:

  • /Products/Product/ProductA
  • /Products/Category/CategoryA

 

public class MyStartupHandler : IApplicationEventHandler
{
    public void OnApplicationStarted(
        UmbracoApplicationBase umbracoApplication, 
        ApplicationContext applicationContext)
    {
        //Create a custom route
        RouteTable.Routes.MapRoute(
            "test",
            "Products/{action}/{id}",
            new
                {
                    controller = "MyProduct", 
                    action = "Product", 
                    id = UrlParameter.Optional
                });           
    }
    public void OnApplicationInitialized(
        UmbracoApplicationBase umbracoApplication, 
        ApplicationContext applicationContext)
    {
    }
    public void OnApplicationStarting(
        UmbracoApplicationBase umbracoApplication, 
        ApplicationContext applicationContext)
    {
    }
}

Create the controller

With the above route in place, I need to create a controller called “MyProductController”. The base class it will inherit from will be “Umbraco.Mvc.PluginController”. This abstract class exposes many of the underlying Umbraco objects that I might need to work with such as an UmbracoHelper, UmbracoContext, ApplicationContext, etc… Also note that the PluginController doesn’t get auto-routed like a SurfaceController which is good because we only want to route our controller once. In 6.1 you can inherit from a different controller called Umbraco.Mvc.UmbracoController, which is what the PluginController will be inheriting from in the next version.

Constructor

First thing is to define the constructors since the PluginController doesn’t have an empty constructor but we want ours to (unless you have IoC setup).

public class MyProductController : PluginController
{
    public MyProductController()
        : this(UmbracoContext.Current)
    {            
    }

    public MyProductController(UmbracoContext umbracoContext) 
        : base(umbracoContext)
    {
    }
}

Actions

Next we need to create the controller Actions. These actions will need to lookup either a Product or a Category based on the ‘id’ string they get passed. For example, given the following URL: /Products/Category/CategoryA the id would be CategoryA and it would execute on the Category action.

In my Umbraco installation, I have 2 document types with aliases: “Product” and “ProductCategory”

image

To perform the lookup in the controller Actions we’ll use the UmbracoHelper.TypedSearch overload which uses Examine.

public ActionResult Category(string id)
{
    var criteria = ExamineManager.Instance.DefaultSearchProvider
        .CreateSearchCriteria("content");
    var filter = criteria.NodeTypeAlias("ProductCategory").And().NodeName(id);
    var result = Umbraco.TypedSearch(filter.Compile()).ToArray();
    if (!result.Any())
    {
        throw new HttpException(404, "No category");
    }
    return View("ProductCategory", CreateRenderModel(result.First()));
}

public ActionResult Product(string id)
{
    var criteria = ExamineManager.Instance.DefaultSearchProvider
        .CreateSearchCriteria("content");
    var filter = criteria.NodeTypeAlias("Product").And().NodeName(id);
    var result = Umbraco.TypedSearch(filter.Compile()).ToArray();
    if (!result.Any())
    {
        throw new HttpException(404, "No product");
    }
    return View("Product", CreateRenderModel(result.First()));
}

The Category action lookup uses Examine to lookup any document with:

  • A document type alias of “ProductCategory”
  • A name equal to the id parameter passed in

The Product action lookup uses Examine to lookup any document with:

  • A document type alias of “Product”
  • A name equal to the id parameter passed in

The result from TypedSearch is IEnumerable<IPublishedContent> and since we know we only want one result we pass in the first item of the collection in “result.First()”

If you didn’t want to use Examine to do the lookup, you could use a Linq query based on the result of Umbraco.TypedContentAtRoot(), but I wouldn’t recommend that since it will be much slower.

In v6.1 the UmbracoHelper exposes a couple of other methods that you could use to perform your lookup if you didn’t want to use Examine and wanted to use XPath instead:

  • TypedContentSingleAtXPath(string xpath, params XPathVariable[] vars)
  • TypedContentAtXPath(string xpath, params XPathVariable[] vars)
  • TypedContentAtXPath(XPathExpression xpath, params XPathVariable[] vars)

CreateRenderModel method

You will have noticed that I’m using a method called CreateRenderModel to create the model that is passed to the View. This method accepts an IPublishedContent object as an argument and creates a RenderModel object which is what a normal Umbraco view expects. This method isn’t complex but it does have a couple things worth noting:

private RenderModel CreateRenderModel(IPublishedContent content)
{
    var model = new RenderModel(content, CultureInfo.CurrentUICulture);

    //add an umbraco data token so the umbraco view engine executes
    RouteData.DataTokens["umbraco"] = model;

    return model;
}

The first thing is that you need to construct the RenderModel with an explicit culture otherwise you’ll get an exception. The next line adds the created RenderModel to the RouteData.DataTokens… this is because we want to render an Umbraco view which will be stored in either of the following places (based on Umbraco standard practices):

  • ~/Views/Product.cshtml
  • ~/Views/ProductCategory.cshtml

These locations are not MVC standard practices. Normally MVC will look in a controller specific folder for views. For this custom controller the locations would be:

  • ~/Views/MyProduct/Product.cshtml
  • ~/Views/MyProduct/ProductCategory.cshtml

But we want to use the views that Umbraco has created for us so we need to ensure that the built in Umbraco ViewEngine gets executed. For performance reasons the Umbraco RenderViewEngine will not execute for a view unless a RenderModel instance exists in the RouteData.DataTokens with a key of “umbraco”, so we just add it there before we return the view.

Views

The views are your regular Umbraco views but there’s a few things that might not work:

  • Macros. Sorry, since we’ve bypassed the Umbraco routing pipeline which macros rely upon, any call to Umbraco.RenderMacro will fail. But you should be able to achieve what you want with Partial Views or Child Actions.
  • Umbraco.Field. Actually this will work but you’ll need to upgrade to 6.0.7 or 6.1.2 based on this fixed issue: http://issues.umbraco.org/issue/U4-2324

One cool thing is that you can use the regular MVC UrlHelper to resolve the URLs of your actions, since this custom controller is actually just a regular old MVC controller after all.

These view example are nothing extraordinary, just demonstrating that they are the same as Umbraco templates with the same model (but using our custom URLs)

ProductCategory

@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
    Layout = null;
}
<html>
    <body>
        <h1>Product category</h1>
        <hr />
        <h2>@Model.Content.Name</h2>
        <ul>
            @foreach (var product in Model.Content.Children
                .Where(x => x.DocumentTypeAlias == "Product"))
            {
                <li><a href="@Url.Action("Product", "MyProduct", new { id = product.Name })">
                        @product.Name
                    </a>
                </li>
            }
        </ul>
    </body>
</html>

Which looks like this:

image

Product

@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
    Layout = null;
}
<html>
    <body>

        <h1>Product</h1>
        <hr />
        <h2>@Model.Content.Name</h2>
        <div>
            @(Model.Content.GetPropertyValue("Description"))
        </div>
    </body>
</html>

Which looks like this:

image

Whats next?

With the setup above you should be able to achieve most of what you would want with custom routing, controllers, URLs and lookups. However, as I mentioned before things like executing Macros and potentially other internal Umbraco bits that rely on objects like the PublishedContentRequest will not work.

Of course if there is a will, there is a way and I have some cool ideas that could make all of those things work seamlessly too with custom MVC routes. Stay tuned!