Shazwazza

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

Razor + dynamic + internal + interface & the 'object' does not contain a definition for 'xxxx' exception

April 11, 2013 14:49 by Shannon Deminick

I’ve come across this strange issue and decided to blog about it since I can’t figure out exactly why this is happening it is clearly something to do with the DLR and interfaces.

First, if you are getting the exception: 'object' does not contain a definition for 'xxxx' it is related to either an object you have being marked internal or you are are using an anonymous object type for your model (which .Net will always mark as internal).

Here’s a really easy way to replicate this:

1. Create an internal model

internal class MyModel
{
public string Name {get;set;}
}

2. Return this model in your MVC action

public ActionResult Index()
{
return View(new InternalTestModel("Shannon"));
}

3. Make your view have a dynamic model and then try to render the model’s property

@model dynamic
<h1>@Model.Name</h1>

You’ll get the exception:

Server Error in '/' Application.

'object' does not contain a definition for 'Name'

So even though the error is not very informative, it makes sense since Razor is trying to access an internal class.

Try using a public interface

Ok so if we want to keep our class internal, we could expose it via a public interface. Our code might then look like this:

public interface IModel 
{
string Name {get;}
}
internal class MyModel : IModel
{
public string Name {get;set;}
}

Then we can change our view to be strongly typed like:

@model IModel
<h1>@Model.Name</h1>

And it will work, however if you change your view back to be @model dynamic you will still get the exception. Pretty sure it’s because the DLR is just binding to the instance object and doesn’t really care about the interface… this makes sense.

Try using an abstract public class

For some reason if you make your internal class inherit from a public abstract class that implements the same interface you will not get this exception even though the object instance you are passing to razor is internal. For example with this structure:

public interface IModel 
{
string Name {get;}
}
public abstract class ModelBase : IModel
{
public abstract Name {get;}
}
internal class MyModel : IModel
{
public override string Name {get;set;}
}

You will not get this error if your view is @model dynamic.

Strange!

I’m sure there’s something written in the DLR spec about this but still seems strange to me! If you are getting this error and you aren’t using anonymous objects for your model, hopefully this might help you figure out what is going on.

Using IoC with Umbraco & MVC

October 31, 2012 17:08 by Shannon Deminick

The question was asked on my post yesterday about the upcoming Umbraco 4.10.0 release with MVC support and whether it is possible to use IoC/Dependency Injection with our implementation. The answer is definitely yes!

One of the decisions we’ve made for the code of Umbraco is to not use IoC in the core. This is not because we don’t like IoC (in fact I absolutely love it) but more because things start to get really messy when not 100% of your developers understand it and use it properly. Since Umbraco is open source there are developers from many walks of life committing code to the core and we’d rather not force a programming pattern on them. Additionally, if some developers don’t fully grasp this pattern this leads to strange and inconsistent code and even worse if developers don’t understand this pattern then sometimes IoC can be very difficult to debug.

This ultimately means things are better for you since we won’t get in the way with whatever IoC framework you choose.

Which frameworks can i use?

Theoretically you can use whatever IoC framework that you’d like, though I haven’t tested or even tried most of them I’m assuming if they are reasonable frameworks that work with MVC then you should be fine. I’m an Autofac fan and to be honest I’ve only dabbled in other frameworks so all examples in this post and documentation are based on Autofac. Since we don’t use any IoC, it also means that we are not specifying a DependencyResolver so you are free to set this to whatever you like (I’d assume that most IoC frameworks would integrate with MVC via the DependencyResolver).

How do i do it?

I chucked up some docs on github here which I’ll basically just reiterate on this post again with some more points. Assuming that you construct your IoC container in your global.asax, the first thing you’ll have to do is to create this class and make sure it inherits from the Umbraco one (otherwise nothing will work). Then just override OnApplicationStarted and build up your container. Here’s an example (using Autofac):

/// <summary>
/// The global.asax class
/// </summary>
public class MyApplication : Umbraco.Web.UmbracoApplication
{
    protected override void OnApplicationStarted(object sender, EventArgs e)
    {
        base.OnApplicationStarted(sender, e);

        var builder = new ContainerBuilder();

        //register all controllers found in this assembly
        builder.RegisterControllers(typeof(MyApplication).Assembly);

        //add custom class to the container as Transient instance
        builder.RegisterType<MyAwesomeContext>();

        var container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    }
}

Notice that I’ve also registered a custom class called MyAwesomeContext in to my container, this is just to show you that IoC is working. Of course you can do whatever you like with your own container :) Here’s the class:

public class MyAwesomeContext
{
    public MyAwesomeContext()
    {
        MyId = Guid.NewGuid();
    }
    public Guid MyId { get; private set; }
}

Next we’ll whip up a custom controller to hijack all routes for any content item that is of a Document Type called ‘Home’ (there’s documentation on github about hijacking routes too):

public class HomeController : RenderMvcController
{
    private readonly MyAwesomeContext _myAwesome;

    public HomeController(MyAwesomeContext myAwesome)
    {
        _myAwesome = myAwesome;
    }

    public override ActionResult Index(Umbraco.Web.Models.RenderModel model)
    {
        //get the current template name
        var template = this.ControllerContext.RouteData.Values["action"].ToString();
        //return the view with the model as the id of the custom class
        return View(template, _myAwesome.MyId);
    }
}

In the above controller, a new instance of MyAwesomeContext will be injected into the constructor, in the Index action we’re going to return the view that matches the currently routed template and set the model of the view to the id of the custom MyAwesomeContext object (This is just an example, you’d probably do something much more useful than this).

We can also do something similar with SurfaceControllers (or any controller you like):

public class MyTestSurfaceController : SurfaceController
{
    private readonly MyAwesomeContext _myAwesome;

    public MyTestSurfaceController(MyAwesomeContext myAwesome)
    {
        _myAwesome = myAwesome;
    }

    [ChildActionOnly]
    public ActionResult HelloWorld()
    {
        return Content("Hello World! Here is my id " + _myAwesome.MyId);
    }
}

That’s it?

Yup, these are just examples of creating controllers with IoC, the actual IoC setup is super easy and should pretty much work out of the box with whatever IoC framework you choose. However, you should probably read the ‘Things to note’ in the documentation in case your IoC engine of choice does something wacky with the controller factory.

Native MVC support in Umbraco coming very soon!

October 30, 2012 23:07 by Shannon Deminick

Its been a while since writing a blog post! … but that’s only because I can never find the time since I’m still gallivanting around the globe :P

But this post is about something very exciting, and I’m sure most of you that are reading this already know that with the upcoming Umbraco 4.10.0 release (currently in Beta and downloadable here) we are natively supporting ASP.Net MVC! What’s more is that I’ve tried to document as much as I could on our GitHub docs. Once my pull request is completed the docs will all be available on the main site but until then you can see them on my fork here.

So where to begin? Well, I’m going to try to keep this really short and sweet because I’m hoping you can find most of the info that you’ll want in the documentation.

What is supported?

Everything that you can do in MVC you will be able to do in Umbraco’s MVC implementation. Anything from Partial Views, Child Actions, form submissions, data annotations, client validation to Surface Controllers, custom routes and hijacking Umbraco routes.  If you have used Razor macros before, we support a very similar syntax but it is not 100% the same. We support most of the dynamic querying that you use in Razor macros with the same syntax but to access the dynamic model is slightly different. What is crazy awesome super cool though is that we support a strongly typed query structure!! :) So yes, you can do strongly typed Linq queries with intellisense, no problemo!

Still using Web forms? not a problem, you can have both Web forms and MVC engines running in tandem on your Umbraco site. However, a config setting will set a default rendering engine which will determine whether Web forms master pages or MVC views are created in the back office.

What is not supported (yet)

There’s a few things in this release that we’ve had to push to 4.11.0 due to time constraints. They include: Better tooling support for the View editors in the back office, Partial View Macros and Child Action Macros. Though once you start using this MVC version you’ll quickly discover that the need for macros is pretty small. Perhaps people use macros in different ways but for the most part with the way that MVC works I would probably only use macros for rendering macro content in the WYSIWYG editor.

We support rendering any macros in MVC so you can even render your XSLT macros in your views, but issues will arise if you have User Control or Web form control macros that contain form elements. You will still be able to render these macros but any post backs will not work, and will most likely cause a YSOD. Unfortunately due to the vast differences between how Web forms and MVC work in ASP.Net this is something that we cannot support. The good news is that creating forms in MVC is super easy and makes a whole lot more sense than Web forms…. you can even have more than one <form> element on a page, who’d have thought :P

Strongly typed queries

You can find the documentation for this here but I just wanted to point out some of the differences between the strongly typed syntax and the dynamic syntax. First, in your view you have two properties:

  • @Model.Content = the strongly typed model for your Umbraco view which is of type: Umbraco.Core.Models.IPublishedContent
  • @CurrentPage = the dynamic model representing the current page, this is very very similar to the @Model property in Razor macros

An example is to get the current page’s children that are visible, here’s the syntax for both (and of course since the @CurrentPage is dynamic, you will not get intellisense):

//dynamic access
@CurrentPage.Children.Where("Visible")

//strongly typed access
@Model.Content.Children.Where(x => x.IsVisible())

There are also some queries that are just not (yet) possible in the dynamic query structure. In order to get some complex queries to work with dynamic linq, we need to code these in to the parser to create the expression tree. The parser could probably do with more TLC to support things like this but IMHO, we’re just better off using the strongly typed way instead (plus its probably a much faster execution). I’ve listed this as an example in the docs but will use it here again:

//This example gets the top level ancestor for the current node, and then gets 
//the first node found that contains "1173" in the array of comma delimited
//values found in a property called 'selectedNodes'.
//NOTE: This is one of the edge cases where this doesn't work with dynamic execution but the
//syntax has been listed here to show you that its much easier to use the strongly typed query
//instead

//dynamic access
var paramVals = new Dictionary<string, object> {{"splitTerm", new char[] {','}}, {"searchId", "1173"}};
var result = @CurrentPage.Ancestors().OrderBy("level")
.Single()
.Descendants()
.Where("selectedNodes != null && selectedNodes != String.Empty && selectedNodes.Split(splitTerm).Contains(searchId)", paramVals)
.FirstOrDefault();

//strongly typed
var result = @Model.Content.Ancestors().OrderBy(x => x.Level)
.Single()
.Descendants()
.FirstOrDefault(x => x.GetPropertyValue("selectedNodes", "").Split(',').Contains("1173"));

IMHO i much prefer the strongly typed syntax but it’s up to you to decide since we support both structures.

UmbracoHelper

Another class we’ve introduced is called the Umbraco.Web.UmbracoHelper which is accessible on your views by using the @Umbraco syntax and is also accessible on any SurfaceController. This class contains a ton of handy methods, it is basically the ‘new’ Umbraco ‘library’ class (you know that static one that has a lower case ‘l’ :P ) Of course the ‘library’ class will still be around and you can still use it in your views… but you shouldn’t! This new helper class should contain all of the methods that you need from querying content/media by ids, rendering macros and rendering field content, to stripping the html from a string. The library class was designed for use in Xslt where everything from c# wasn’t natively given to you, now with Razor views you have the entire world of c# at your fingertips. So you’ll notice things like the date formatting functions that were on ‘library’ are not on the UmbracoHelper, and that is because you can just use the regular c# way. Though, if you feel inclined that these methods should be on UmbracoHelper, another great thing is that this is not a static class so you can add as many extension methods as you like.  I’d list all of the methods out here but I said I’d keep it short, your best bet currently is to just have a look at the class in the source code, or just see what great stuff shows up in intellisense in VS.

UmbracoContext

Though we did have another UmbracoContext class, this one is the new cooler one and the old one has been marked as obsolete. This new one’s full name is Umbraco.Web.UmbracoContext and it is a singleton so you can access it by UmbracoContext.Current but normally you shouldn’t have to access it by it’s singleton because it is available in all of your views and SurfaceControllers as a property. For example, to access the UmbracoContext in your view you simply use @UmbracoContext. This class exposes some handy properties like: UmbracoUser, PageId, IsDebug, etc…

Testing

If you are interested in MVC and Umbraco, it would be really really appreciated for you to take the time to download the beta, the latest nightlies or source code and give it a shot. Hopefully the docs are enough to get you up and running and if you run into any troubles please log your issues on the tracker. If you have any question, comments, etc… about the source code we’d love to hear from you on the mail list.

Pete will be putting up an MVC getting started post on the Umbraco blog real soon, so watch this space!

Adios!

Taming the BuildManager, ASP.Net Temp files and AppDomain restarts

March 28, 2012 06:14 by Shannon Deminick

I’ve recently had to do a bunch of research in to the BuildManager and how it deals with caching assemblies since my involvement with creating an ASP.Net plugin engine. Many times people will attempt to restart their AppDomain by ‘bumping’ their web.config files, meaning adding a space character or carriage return and then saving it. Sometimes you may have noticed that this does not always restart your AppDomain the way you’d expect and some of the new types you’d expect to have loaded in your AppDomain simply aren’t there. This situation has cropped up a few times when using the plugin engine that we have built in to Umbraco v5 and some people have resorted to manually clearing out their ASP.Net temp files and then forcing an IIS Restart, but this is hardly ideal. The good news is that we do have control over how these assemblies get refreshed, in fact the BuildManager reloads/refreshes/clears assemblies in different ways depending on how the AppDomain is restarted.

The hash.web file

An important step during the BuildManager initialization phase is a method call to BuildManager.CheckTopLevelFilesUpToDate which does some interesting things. First it checks if a special hash value is on disk and is not zero. You may have noticed a file in your ASP.Net temp files: /hash/hash.web and it will contain a value such as: 4f34227133de5346. This value represents a unique hash of many different combined objects that the BuildManager monitors. If this file exists and the value can be parsed properly then the BuildManager will call: diskCache.RemoveOldTempFiles(); What this does is the following:

  • removes the CodeGen temp resource folder
  • removes temp files that have been saved during CodeGen such as *.cs
  • removes the special .delete files and their associated original files which have been created when shutting down the app pool and when the .dll files cannot be removed (due to file locking)

Creating the hash value

The next step is to create this hash value based on the current objects in the AppDomain. This is done using an internal utility class in the .Net Framework called: HashCodeCombiner (pretty much everything that the BuildManager references is marked Internal! ). The process combines the following object values to create the hash (I’ve added in parenthesis the actual properties the BuildManager references):

  • the app's physical path, in case it changes (HttpRuntime.AppDomainAppPathInternal)
  • System.Web.dll (typeof(HttpRuntime).Module.FullyQualifiedName)
  • machine.config file name (HttpConfigurationSystem.MachineConfigurationFilePath)
  • root web.config file name, please note that this is not your web apps web.config (HttpConfigurationSystem.RootWebConfigurationFilePath)
  • the hash of the <compilation> section (compConfig.RecompilationHash)
  • the hash of the system.web/profile section (profileSection.RecompilationHash)
  • the file encoding set in config (appConfig.Globalization.FileEncoding)
  • the <trust> config section (appConfig.Trust.Level & appConfig.Trust.OriginUrl)
  • whether profile is enabled (ProfileManager.Enabled)
  • whether we are precompiling with debug info (but who precompiles :) (PrecompilingWithDebugInfo)

Then we do a check for something I didn’t know existed called Optimize Compilations which will not actual affect the hash file value for the following if it is set to true (by default is is false):

  • the ‘bin’ folder (HttpRuntime.BinDirectoryInternal)
  • App_WebReferences (HttpRuntime.WebRefDirectoryVirtualPath)
  • App_GlobalResources  (HttpRuntime.ResourcesDirectoryVirtualPath)
  • App_Code (HttpRuntime.CodeDirectoryVirtualPath)
  • Global.asax (GlobalAsaxVirtualPath)

Refreshing the ASP.Net temp files (CodeGen files)

The last step of this process is to check if the persisted hash value in the hash.web file equals the generated hash value from the above process. If they do not match then a call is made to diskCache.RemoveAllCodegenFiles(); which will:

  • clear all codegen files, removes all files in folders but not the folders themselves,
  • removes all root level files except for temp files that are generated such as .cs files, etc...

This essentially clears your ASP.Net temp files completely, including the MVC controller cache file, etc…

Then the BuildManager simply resaves this calculated has back to the hash.web file.

What is the best way to restart your AppDomain?

There is really no ‘best’ way, it just depends on your needs. If you simply want to restart your AppDomain and not have the overhead of having your app recompile all of your views and other CodeGen classes then it’s best that you simply ‘bump’ your web.config by just adding a space, carriage return or whatever. As you can see above the hash value is not dependant on your local web.config file’s definition (timestamp, etc…). However, the hash value is dependent on some other stuff in your apps configuration such as the <compilation> section, system.web/profile section, the file encoding configured, and the <trust> section. If you update any value in any of these sections in your web.config it will force the BuildManager to clear all cached CodeGen files which is the same as clearing your ASP.Net temp files.

So long as you don’t have optimizeCompilations set to true, then the easiest way to clear your CodeGen files is to simply add a space or carriage return to your global.asax file or modify something in the 4 other places that the BuildManager checks locally: App_Code, App_GlobalResources, App_WebResources, or modify/add/remove a file in your bin folder.

How does this affect the ASP.Net plugin engine?

Firstly, i need to update that post as the code in the plugin engine has changed quite a bit but you can find the latest in the Umbraco source code on CodePlex. With the above knowledge its easy to clear out any stale plugins by perhaps bumping your global.asax file, however this is still not an ideal process. With the research I’ve done in to the BuildManager I’ve borrowed some concepts from it and learned of a special “.delete” file extension that the BuildManager looks for during AppDomain restarts. With this new info, I’ve already committed some new changes to the PluginManager so that you shouldn’t need to worry about stale plugin DLLs.

Sharing Controller Actions with ControllerExtenders

October 4, 2011 12:17 by Shannon Deminick

Why?

If you wanted to be able to share Actions between controllers in .Net there is currently no official way to do it and that is probably because it might not be something that people have thought about doing very often before. Though once I started thinking about it I realized that this concept could be used for a variety of different purposes and that other people might potentially think of whole new ways to apply this concept. Here’s a couple use cases:

  • Separating logic out of large controllers
  • Ability to distribute Controllers in DLLs whose Actions that could then be consumed by other people’s Controllers
  • Support for a ‘Multiple inheritance’ class structure

Again I think that there is some potential here for people to run with this concept in their own ways. Its probably not a concept that everyone will want/need to use, but you should know that it definitely can be done, and here’s how…

ControllerExtender

In order to extend your Controller with actions from another Controller, you’ll need to register your extension with a new class called ControllerExtender. There’s a couple of ways to go about doing this, the nicest way is to use attributes on your Controller:

[ExtendedBy(typeof(TestExtenderController))]
public class ContentEditorController : Controller
{....}

Otherwise there’s a few overloads on the ControllerExtender class to directly do this:

//Where 'this' is the controller you are extending, generally called in the 
//constructor of your controller
ControllerExtender.RegisterExtender(this, typeof (TestExtenderController));

//Where 'this' is the controller you are extending, generally called in the 
//constructor of your controller
ControllerExtender.RegisterExtender<TestExtenderController>(this);

//This registration could be created in your global.asax 
ControllerExtender.RegisterExtender<ContentEditorController, TestExtenderController>();

One important thing to note is that the ControllerExtender uses the DependencyResolver to create an instance of the extending controller class so you’ll need to ensure that your controllers are registered properly in IoC.

Illegal Extenders

The ControllerExtender will not let you extend a controller by it’s same type of it’s same sub type. Also, nested extenders do not work (though the code could be modified to support this) therefore you cannot have Controller ‘A’ be extended by Controller ‘B’ which is extended by Controller ‘C’. In this scenario when rendering actions on Controller ‘A’, only actions on Controller ‘A’ and ‘B’ will be resolved. When rendering actions on Controller ‘B’ only actions on Controller ‘B’ and ‘C’ will be resolved.

Multiple Extenders

You can register multiple extenders for one Controller but because multiple extenders may have the same Action name/signature, only the first one that is found that is a match is used. It is therefor up to the developer to make sure they are aware of this.

Custom IActionInvoker

In order to facilitate the sharing of Actions a custom IActionInvoker of type ControllerExtenderActionInvoker needs to be registered on your controller. This is easy to do in the constructor of your controller:

public MyController()
{
    this.ActionInvoker = new ControllerExtenderActionInvoker();
}

Source Code

The source code for all of this is pretty straight forward and boils down to 3 classes:

  • The static ControllerExtender class
  • The custom ControllerExtenderActionInvoker
  • The ExtendedByAttribute

ControllerExtenderActionInvoker class

This class is responsible for finding and invoking the correct Action on a controller which takes into account any of the extenders registered for the currently executing Controller.

The most up to date code can be found in the Umbraco 5 source code HERE.

ControllerExtender class

This class is responsible for maintaining the extender registrations, it allows for creating registrations and returning registrations:

public static class ControllerExtender
{

    /// <summary>
    /// Internal ConcurrentDictionary to store all registrations
    /// </summary>
    private static readonly ConcurrentDictionary<Tuple<Type, Type>, Func<ControllerBase>> 
        Registrations
            = new ConcurrentDictionary<Tuple<Type, Type>, Func<ControllerBase>>();

    /// <summary>
    /// Registers the extender.
    /// </summary>
    /// <typeparam name="TController">The type of the controller.</typeparam>
    /// <typeparam name="TExtender">The type of the extender.</typeparam>
    public static void RegisterExtender<TController, TExtender>()
        where TController : ControllerBase
        where TExtender : ControllerBase
    {
        var t = new Tuple<Type, Type>(typeof(TController), typeof(TExtender));
        if (Registrations.ContainsKey(t))
            return;

        if (typeof(TExtender).IsAssignableFrom(typeof(TController)))
        {
            throw new InvalidOperationException
                ("Cannot extend a controller by it's same type");
        }

        Registrations.TryAdd(t, () => DependencyResolver.Current.GetService<TExtender>());
    }

    /// <summary>
    /// Registers the extender.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="controllerToExtend">The controller to extend.</param>
    public static void RegisterExtender<T>(
        ControllerBase controllerToExtend)
        where T : ControllerBase
    {
        RegisterExtender(controllerToExtend, () => DependencyResolver.Current.GetService<T>());
    }

    /// <summary>
    /// Registers the extender.
    /// </summary>
    /// <param name="controllerToExtend">The controller to extend.</param>
    /// <param name="controllerExtender">The controller extender.</param>
    public static void RegisterExtender(
        ControllerBase controllerToExtend, 
        Type controllerExtender)
    {
        var t = new Tuple<Type, Type>(controllerToExtend.GetType(), controllerExtender);
        if (Registrations.ContainsKey(t))
            return;

        if (controllerExtender.IsAssignableFrom(controllerToExtend.GetType()))
        {
            throw new InvalidOperationException
                ("Cannot extend a controller by it's same type");
        }

        Registrations.TryAdd(t, 
            () => DependencyResolver.Current.GetService(controllerExtender) as ControllerBase);
    }

    /// <summary>
    /// Registers the extender.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="controllerToExtend">The controller to extend.</param>
    /// <param name="extender">The extender.</param>
    public static void RegisterExtender<T>(
        ControllerBase controllerToExtend, 
        Expression<Func<T>> extender)
        where T : ControllerBase
    {
        var extenderType = typeof(T);
        var t = new Tuple<Type, Type>(controllerToExtend.GetType(), extenderType);
        if (Registrations.ContainsKey(t))
            return;

        if (extender.GetType().IsAssignableFrom(controllerToExtend.GetType()))
        {
            throw new InvalidOperationException
                ("Cannot extend a controller by it's same type");
        }

        Registrations.TryAdd(t, extender.Compile());
    }

    /// <summary>
    /// Returns all registrations as a readonly collection
    /// </summary>
    /// <returns></returns>
    public static IEnumerable<KeyValuePair<Tuple<Type, Type>, Func<ControllerBase>>> 
        GetRegistrations()
    {
        return Registrations;
    }

}

ExtendedByAttribute class

This Attribute is used by the ControllerExtenderActionInvoker to created Extender registrations dynamically.

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ExtendedByAttribute : Attribute
{
    public Type ControllerExtenderType { get; private set; }

    public ExtendedByAttribute(Type controllerExtenderType)
    {
        if (!typeof(ControllerBase).IsAssignableFrom(controllerExtenderType))
        {
            throw new ArgumentException
                ("controllerExtenderType must be of type Controller");
        }
        ControllerExtenderType = controllerExtenderType;       
    }
}

Sharing views between specific controllers

July 21, 2011 01:29 by Shannon Deminick

There’s no native way in MVC to share views between controllers without having to put those views in the ‘Shared’ folder which can be annoying especially if you have an inherited controller class structure, or your simply wish to just have one particular controller look for views in the folder of a different controller.

With a little help from DotPeek to see what’s going on inside of the VirtualPathProviderViewEngine, I’ve created a fairly elegant solution to the above problem which consists of a custom ViewEngine and a custom controller Attribute. Lets start with the custom Attribute, its really simple and only contains one property which is the name of the controller that you wish the attributed controller to reference views.  An example:

[AlternateViewEnginePath("ContentEditor")]
public class MediaEditorController : Controller
{  ...  }

The example above is saying that we have a MediaEditor controller that should reference views in the ContentEditor controller’s view folder, pretty simple right! A cool part about how the underlying ViewEngine works is that if a view isn’t found in the ContentEditor controller’s folder, then it will check back to the current controller’s folder, so it has built in fall back support.

The custom ViewEngine is actually pretty simple as well once you know what’s going on inside of the VirtualPathProviderViewEngine. There’s two particular methods: FindView and FindPartialView which the VirtualPathProviderViewEngine figures out which controller/folder to look in. It figures this out by looking at the current ControllerContext’s RouteValues and simply does a lookup of the controller name by using this statement:

controllerContext.RouteData.GetRequiredString("controller");

So all we’ve got to do is override the FindView and FindPartialView methods, create a custom ControllerContext with the “controller” route value to be the value specified in our custom attribute, and then pass this custom ControllerContext into the underlying base FindView/FindPartial view methods:

/// <summary>
/// A ViewEngine that allows a controller's views to be shared with other 
/// controllers without having to put these shared views in the 'Shared' folder.
/// This is useful for when you have inherited controllers.
/// </summary>
public class AlternateLocationViewEngine : RazorViewEngine
{
    public override ViewEngineResult FindPartialView(
        ControllerContext controllerContext, 
        string partialViewName, 
        bool useCache)
    {
        var altContext = GetAlternateControllerContext(controllerContext);
        if (altContext != null)
        {
            //see if we can get the view with the alternate controller 
            //specified, if its found return the result, if its not found
            //then return the normal results which will try to find 
            //the view based on the 'real' controllers name.
            var result = base.FindPartialView(altContext, partialViewName,useCache);
            if (result.View != null)
            {
                return result;
            }
        }
        return base.FindPartialView(controllerContext, partialViewName,useCache);
    }

    public override ViewEngineResult FindView(
        ControllerContext controllerContext, 
        string viewName, 
        string masterName, 
        bool useCache)
    {
        var altContext = GetAlternateControllerContext(controllerContext);
        if (altContext!= null)
        {
            var result = base.FindView(altContext, viewName, masterName, useCache);
            if (result.View != null)
            {
                return result;
            }
        }
        return base.FindView(controllerContext, viewName, masterName, useCache);
    }

    /// <summary>
    /// Returns a new controller context with the alternate controller name in the route values
    /// if the current controller is found to contain an AlternateViewEnginePathAttribute.
    /// </summary>
    /// <param name="currentContext"></param>
    /// <returns></returns>
    private static ControllerContext GetAlternateControllerContext(
        ControllerContext currentContext)
    {
        var controller = currentContext.Controller;
        var altControllerAttribute = controller.GetType()
            .GetCustomAttributes(typeof(AlternateViewEnginePathAttribute), false)
            .OfType<AlternateViewEnginePathAttribute>()
            .ToList();
        if (altControllerAttribute.Any())
        {
            var altController = altControllerAttribute.Single().AlternateControllerName;
            //we're basically cloning the original route data here...
            var newRouteData = new RouteData
                {
                    Route = currentContext.RouteData.Route,
                    RouteHandler = currentContext.RouteData.RouteHandler
                };
            currentContext.RouteData.DataTokens
                .ForEach(x => newRouteData.DataTokens.Add(x.Key, x.Value));
            currentContext.RouteData.Values
                .ForEach(x => newRouteData.Values.Add(x.Key, x.Value));

            //now, update the new route data with the new alternate controller name
            newRouteData.Values["controller"] = altController;

            //now create a new controller context to pass to the view engine
            var newContext = new ControllerContext(
                currentContext.HttpContext, 
                newRouteData, 
                currentContext.Controller);
            return newContext;
        }

        return null;
    }
}

/// <summary>
/// An attribute for a controller that specifies that the ViewEngine 
/// should look for views for this controller using a different controllers name.
/// This is useful if you want to share views between specific controllers 
/// but don't want to have to put all of the views into the Shared folder.
/// </summary>
public class AlternateViewEnginePathAttribute : Attribute
{
    public string AlternateControllerName { get; set; }

    public AlternateViewEnginePathAttribute(string altControllerName)
    {
        AlternateControllerName = altControllerName;
    }
}
Lastly, you’ll need to just register this additional ViewEngine in your global.asax, or IoC, or however you are doing that sort of thing in your application.

Umbraco Jupiter Plugins - Part 2 - Routing

May 11, 2011 08:26 by Shannon Deminick

This is the second blog post in a series of posts relating to building plugins for Umbraco v5 (Jupiter).

Related Posts:

  1. Umbraco Jupiter Plugins – Part 1

Disclaimer

This post is about developing for Umbraco v5 (Jupiter) which at the time of this post is still under development. The technical details described below may change by the time Umbraco Jupiter is released. If you have feedback on the technical implementation details, please comment below.

Routing & URLs

As mentioned in the previous post Umbraco Jupiter will consist of many types of plugins, and of those plugins many of them exist as MVC Controllers.  Each controller has an Action which a URL is routed to, this means that each Controller plugin in Jupiter requires it’s own unique URLs. The good news is that you as a package developer need not worry about managing these URLs and routes, Jupiter will conveniently do all of this for you.

Packages & Areas

My previous post mentioned that a ‘Package’ in Jupiter is a collection of ‘Plugins’ and as it turns out, Plugins can’t really function unless they are part of a Package. In it’s simplest form, a Package in v5 is a folder which contains Plugins that exists in the ~/Plugins/Packages sub folder. The folder name of the package becomes very important because it is used in setting up the routes to  create the unique URLs which map to the MVC Controller plugins. Package developers should be aware that they should name their folder to something that is reasonably unique so it doesn’t overlap with other Package folder names. During package installation, Jupiter will check for uniqueness in Package folder names to ensure there is no overlap (there will be an entirely separate post on how to create deployment packages and how to install them).

Here’s a quick example: If I have a Package that contains a Tree plugin called TestTree (which is in fact an MVC Controller) and I’ve called my Package folder ‘Shazwazza’, which exists at ~/Plugins/Packages/Shazwazza then the URL to return the JSON for the tree is: http://localhost/Umbraco/Shazwazza/Trees/TestTree/Index 

Similarly, if I have a Editor plugin called TestEditor with an action called Edit, then a URL to render the Edit Action is:

http://localhost/Umbraco/Shazwazza/Editors/TestEditor/Edit

If you’ve worked with MVC, you’ll probably know what an MVC Area is. The way that Jupiter creates the routes for Packages/Plugins is by creating an MVC Area for each Package. This is how it deals with the probability that different Package developers may create MVC Controllers with the same name. MVC routes are generally based just on a Controller name and an Action name which wouldn’t really work for us because there’s bound to be overlap amongst Package developers, so by creating an Area for each Package the route becomes unique to a combination of Controller name, Action name and Area name.  MVC also determines where to look for Views based on Area name which solves another issue of multiple Packages installed with the same View names.

Whats next?

In the coming blog posts I’ll talk about

  • how plugins are installed and loaded
  • how and where the Views are stored that the plugins reference
  • how to create all of the different types of plugins

Code Garden 2011

I’ll be doing a talk on Plugins for Umbraco Jupiter at Code Garden 2011 this year which will go in to a lot more detail than these blog posts. If you are attending Code Garden already, then hopefully this series will give you a head start on Umbraco v5. If you haven’t got your tickets to Code Garden yet, what are you waiting for?! We have so much information to share with you :)

Umbraco Jupiter Plugins - Part 1

April 29, 2011 09:55 by Shannon Deminick

This is the first blog post in a series of posts relating to building plugins for Umbraco v5 (Jupiter)

Disclaimer

This post is about developing for Umbraco v5 (Jupiter) which at the time of this post is still under development. The technical details described below may change by the time Umbraco Jupiter is released. If you have feedback on the technical implementation details, please comment below.

What is a plugin

Since Umbraco Jupiter has been built from the ground up, we first need to define some v5 terminology:

  • Plugin = A single functional component that extends Umbraco such as a Tree, an Editor, a Menu Item, etc…
  • Package = A group of Plugins installed into Umbraco via the ~/Plugins/Packages/[Package Name] folder

The Umbraco v5 back-office has been architected to run entirely on Plugins, in fact the core trees, editors, etc… that are shipped with Umbraco are Plugins in v5.

Types of plugins

The list of Plugin types will most likely increase from the time of this blog post to when Jupiter is launched but as it stands now here are the types of Plugins supported in v5:

  • Property Editors
    • This term is new to v5. In v4.x there was no differentiation between a Data Type and it’s underlying ‘Data Type’ editor. In v5 we’ve made a clear distinction between a ‘Data Type’ (as seen in the Data Type tree and used as properties for Document Types) and the underlying editor and pre-value editor that it exposes.  An example of a Property Editor would be uComponents’ Multi-Node Tree Picker. An example of a Data Type would be when an Umbraco administrator creates a new Data Type node in the Data Type tree and assigns the Multi-Node Tree Picker as it’s Property Editor.
    • So uComponents Team, this is where you need to focus your efforts for version 5!
  • Menu Items
    • Context menu items such as Create, Publish, Audit Trail, etc… are all plugins.
    • See this post in regards to creating menu items in v5, though there have been some new features added since that article was created which I’ll detail in coming posts in this series.
  • Editors
    • Editor plugins are all of the interactions performed in the right hand editor panel and in modal dialogs in the back-office.
    • For example, the Content Editor core plugin in v5 manages the rendering for all views such as editing content, sorting, copying, and moving nodes, and nearly all of the other views that the context menu actions render.
    • Editors are comprised mostly of MVC Controllers, Views, JavaScript & CSS.
  • Trees
    • All trees are plugins, for example, the Content tree, Media tree, Document Type tree are all different plugins.
    • Trees are actually MVC controllers.
    • Tree nodes use Menu Items to render Editors
  •  Presentation Addins (Yet to be officially named)
    • Another type of plugin are Controllers that are installed via packages that interact with the presentation layer, an example of this might be the Controller Action that accepts the post values from a Contour form.

Whats next?

In the coming blog posts I’ll talk about

  • how plugins are installed and loaded
  • how the Umbraco routing system works with all of these controllers
  • how and where the Views are stored that the plugins reference
  • how to create all of the different types of plugins

Code Garden 2011

I’ll be doing a talk on Plugins for Umbraco Jupiter at Code Garden 2011 this year which will go in to a lot more detail than these blog posts. If you are attending Code Garden already, then hopefully this series will give you a head start on Umbraco v5. If you haven’t got your tickets to Code Garden yet, what are you waiting for?! We have so much information to share with you :)

HtmlHelper Table methods

April 22, 2011 09:29 by Shannon Deminick

I was hoping that these were built already but couldn’t see any descent results with a quick Google search so figured I’d just write my own… these are now part of the Umbraco v5 codebase. Here’s 2 HtmlHelper methods that allow you to very quickly create an Html table output based on an IEnumerable data structure. Luckily for us, Phil Haack showed us what Templated Razor Delegates are which makes stuff like this very elegant.

There are 2 types of tables: a table that expands vertically (you set a maximum number of columns), or a table that expands horizontally (you set a maximum amount of rows). So I’ve created 2 HtmlHelper extensions for this:

Html.TableHorizontal Html.TableVertical

Say you have a partial view with a declaration of something like:

@model IEnumerable<MyDataObject>

To render a table is just one call and you can customize exactly how each cell is rendered and styled using Razor delegates, in the following example the MyDataObject class contains 3 properties: Name, Enabled and Icon and each cell is going to render a check box with a CSS image as it’s label and there will be a maximum of 8 columns:

@Html.TableVertical(Model, 8, @<text> @Html.CheckBox(@item.Name, @item.Enabled) <label for="@Html.Id(@item.Name)" class="@item.Icon"> <span>@item.Name</span> </label> </text>)

Nice! that’s it, far too easy. One thing to note is the @<text> </text> syntax as this is special Razor parser syntax that declares a Razor delegate without having to render additional html structures. Generally Razor delegates will require some Html tags such as this: @<div> </div> but if you don’t require surrounding Html structures, you can use the special @<text> </text> tags.

Lastly here’s all the HtmlHelper code… I’ve created these extensions as HtmlHelper extensions only for consistency but as you’ll be able to see, the HtmlHelper is never actually used and these extensions could simply be extensions of IEnumerable<T>.

/// <summary> /// Creates an Html table based on the collection /// which has a maximum numer of rows (expands horizontally) /// </summary> public static HelperResult TableHorizontal<T>(this HtmlHelper html, IEnumerable<T> collection, int maxRows, Func<T, HelperResult> template) where T : class { return new HelperResult(writer => { var items = collection.ToArray(); var itemCount = items.Count(); var maxCols = Convert.ToInt32(Math.Ceiling(items.Count() / Convert.ToDecimal(maxRows))); //construct a grid first var grid = new T[maxCols, maxRows]; var current = 0; for (var x = 0; x < maxCols; x++) for (var y = 0; y < maxRows; y++) if (current < itemCount) grid[x, y] = items[current++]; WriteTable(grid, writer, maxRows, maxCols, template); }); } /// <summary> /// Creates an Html table based on the collection /// which has a maximum number of cols (expands vertically) /// </summary> public static HelperResult TableVertical<T>(this HtmlHelper html, IEnumerable<T> collection, int maxCols, Func<T, HelperResult> template) where T : class { return new HelperResult(writer => { var items = collection.ToArray(); var itemCount = items.Count(); var maxRows = Convert.ToInt32(Math.Ceiling(items.Count() / Convert.ToDecimal(maxCols))); //construct a grid first var grid = new T[maxCols, maxRows]; var current = 0; for (var y = 0; y < maxRows; y++) for (var x = 0; x < maxCols; x++) if (current < itemCount) grid[x, y] = items[current++]; WriteTable(grid, writer, maxRows, maxCols, template); }); } /// <summary> /// Writes the table markup to the writer based on the item /// input and the pre-determined grid of items /// </summary> private static void WriteTable<T>(T[,] grid, TextWriter writer, int maxRows, int maxCols, Func<T, HelperResult> template) where T : class { //create a table based on the grid writer.Write("<table>"); for (var y = 0; y < maxRows; y++) { writer.Write("<tr>"); for (var x = 0; x < maxCols; x++) { writer.Write("<td>"); var item = grid[x, y]; if (item != null) { //if there's an item at that grid location, call its template template(item).WriteTo(writer); } writer.Write("</td>"); } writer.Write("</tr>"); } writer.Write("</table>"); }

Using metadata with MEF in Medium Trust

March 3, 2011 18:20 by Shannon Deminick

One of the awesome parts of MEF is that you can attach metadata to your objects. This allows you to use the new Lazy<T, TMetadata> object so you can inspect the meta data about an object without instantiating it. In order to attach metadata to an object, you need (should have) some entity that contains properties to support the meta data such as an interface (good tutorial site here ). For example, if you have a class of type MyObjectType and an interface to store metadata about the class of type IMyMetadata, you can then resolve/import Lazy<MyObjectType, IMyMetadata> which will expose a Metadata property of type IMyMetadata with the information filled in. You might already be able to tell that a bit of magic is happening behind the scenes since there is no concrete class for IMyMetadata but MEF has magically found a way to instantiate a class of type IMyMetadata and fill in the values. This is the problem area if you are running in Medium Trust, you will get a SecurityException stemming form the System.ComponentModel.Composition.MetadataViewGenerator. This same issue will occur when using Autofac to register metadata with the .WithMetadata extension method.

Solution

To work around this problem in Medium Trust, you’ll need to create a concrete class to store your metadata in instead of just referencing an interface. You’ll also need to ensure that the constructor of this class takes in one parameter of type IDictionary<string, object> and using this object you’ll need to manually do the heavy lifting of assigning the values to the properties of this class based on the values in the dictionary. Better yet, you can just make a base class that all your metadata classes can inherit from. In Umbraco v5, we’ve called this abstract class MetadataComposition just to keep inline with the naming conventions of the MEF namespace:

public abstract class MetadataComposition { /// <summary> /// constructor, sets all properties of this object based /// on the dictionary values /// </summary> /// <param name="obj"></param> protected MetadataComposition(IDictionary<string, object> obj) { var t = GetType(); var props = t.GetProperties(); foreach (var a in obj) { var p = props.Where(x => x.Name == a.Key).SingleOrDefault(); if (p != null) { p.SetValue(this, a.Value, null); } } } }

Hope this helps someone!

Recent Tweets

Twitter May 14, 14:14
blogged: script to fix media paths when changing vdirs or media root paths in #umbraco http://t.co/w1csEtceu1

Twitter May 10, 18:27
@darrenferguson probably better discussed on a forum thread I think :-) @sitereactor

Twitter May 10, 17:55
@darrenferguson and won't work in LB cuz a servers file will b stale when it goes to sleep @sitereactor

Twitter May 10, 17:53
@darrenferguson sorry, bad wording... You'd have to write that feature but it'd be pretty simple @drobar @sitereactor

Twitter May 10, 08:34
@sitereactor ... wouldn't work for LB scenarios though @darrenferguson @drobar

Follow me