The ReturnNull Library

The ReturnNull library

Hey guys! first of all, I would like to express thanks for your comments over the last couple of months. Seeing that people are reading this blog has re-sparked my enthusiasm for writing more articles. Thanks all.

Now onto the matter at hand. In a previous post, I promised to put the HiddenRouteConstraint into GitHub and post as a NuGet package. I have decided to write up some useful tools for working with Umbraco and MVC together; this includes the HiddenRouteConstraint, so I guess that covers that.

  • You can find my nuget packages here.
  • You can find my source code in my new GitHub repository.
  • I will be¬†registering to SymbolSource.org so you should be able to gain maximum debuggability if you know how to use external symbol sources with Visual Studio.

I worked hard to ensure I put maximum documentation into my libraries, but for the sake of padding my blog, I will give a description of each package.

ReturnNull.NuGetBuilds

This package is useful as a development tool. Assuming that you are creating libraries yourself, this package will handle the NuGet package creation for you. Install this package and then do a build in release mode. Now you should see your NuGet file in the folder: /.nuget/bin/ relative to your solution.

Of course, nothing is ever as easy as it seems, so there are some assumptions and pre-requisites to mention:

  • You should have a valid nuspec file with the same name as your project beside your project file.
  • Dependencies will be automatically discovered by NuGet using the -IncludeReferencedProjects flag. This has some side effects which I will outline later.
  • NuGet packages will only be built when the build is done in release mode.

There are of course, some benefits:

  • Symbol packages are automatically created. If you have no need for them, you should be able to simply ignore these files.
  • The required folder structures are self managed.
  • Dependencies will be automatically discovered by NuGet using the -IncludeReferencedProjects flag. Yes, it has its benefits too.
  • You don’t need to worry about the nuget.exe file, this package depends on the NuGet.CommandLine package
  • You don’t need to worry about the version of nuget.exe, this package will run the update -Self command before creating your packages so you will always be able to use the latest features of NuGet. If the update fails (eg. no internet) the package should still continue.

IncludeReferencedProjects

This flag is not as well documented online as much of the other options for NuGet. It’s unfortunate, because it can be a really useful tool, I just had to do a fair bit of research and testing before I was comfortable using it.

By using this flag, NuGet will find all project references and installed packages and automatically include them inside your NuGet package. Sounds simple enough right? here are some tips which will help to bring back control:

  • To avoid warnings, don’t double up your dependencies by referring to them within the nuspec file.
  • By default, dependent packages are included with a versioning constraint of larger than or equal to {version}. In cases where you need finer control over determining compatible versions, you can add the allowedVersions to your packages.config file. For example, I knew one of my packages implemented an interface that was defined in a specific version of another package. I also knew that newer versions of that package would alter the interface and make my package incompaible. Therefore, I added a fixed version constraint.
  • If you reference a local project, NuGet will handle this one of two ways. If it finds a nuspec file, NuGet will add a package reference. If there is no matching nuspec file, the projects dll will be included in your own project.
  • For package references, versioning becomes an issue. NuGet will detemine your versioning requirements by reading your nuspec file. If the nuspec file uses a replacement token ($version$), then NuGet will look back at the project for he version number as expected.

Some developers create a script to manage their packages, and they will version all packages the same. For this package, the ideal scenario would be to manage the versioning of each project on an individual basis.

ReturnNull.MvcTools

This package contains the HiddenRouteConstraint that was described in my previous post. For developers who like to be strict when setting up routes, this constraint can help. When setting up routes, every route you create will be accessable via the browser by typing in a url. In some cases, you would like actions to be accessible, but not via url.

The most common example, is when you want to render an action exclusively as a child action. If you do not have a route for that action, MVC will not allow yout to render the action as a child action. Most users use the ChildOnly attribute and accept that as the answer. Unfortunately, if a user of your site typed in the url for your child action, they would recieve a 500 error when some would prefer that they be given a 404 response.

Using this constraint, MVC will ensure that the route is hidden when matching the users request url. Any other time, it will match. Another potential use of this constraint is when routing is not as simple as matching a url; Umbraco provides a nice potential example. Umbraco matches the url against the CMS content and then generates an MVC route which tells the MVC framework which controller and action to use. This route that Umbraco uses is internal and if a user were to type that route as a relative path in the browser, they would see unexpected results which could expose a potential security risk.

Over time, this project will be filled with small code snippets that are designed to aid development in a vanilla MVC application.

ReturnNull.UmbracoApi

This project provides a selection of wrappers which implement easy to mock interfaces. This allows you to use your own IoC container and dependency injection to ensure your controllers and code remains unit testable.

ReturnNull.UmbracoMvcCatalyst

This project is designed to help you provide better integration between Umbraco and MVC. This project has a dependency on ReturnNull.UmbracoApi although if you do not want to use the wrappers, the functionality will still work (for now). It also uses the HiddenRouteConstraint described above.

This project provides two static methods designed to auto-route controllers similarly to how Umbraco auto-routes SurfaceControllers, and help you to share your layout files across both Umbraco pages and MVC pages.

Creating a hijacking controller is not difficult, however, you should remember to set the correct ActionInvoker within the controllers constructor. Just to simplify, I have created the HijackingController abstract class. Extend from this and focus only on your business logic.

I have described why somebody may want to auto-route controllers to use instead of surface controllers in a previous post. Sharing layouts is a new feature which I will explain below.

Sharing Layouts

In a previous project, we have found the need to run a purely MVC driven section of a website along side a CMS driven site. We encountered a problem when we tried to use the same layout for the MVC pages that we used for our umbraco pages.

Firstly, we found that using the @Umbraco.Field method did not work properly. If we used the recursive flag, then we had no problems. However, if we did not use it, then internally, the UmbracoContext was used and if you are rendering an MVC page (not routed by Umbraco), you would encounter an exception. As harsh as it sounds, we decided that we would refrain from using the UmbracoHelper and instead use the UmbracoApi wrappers exclusively.

Next, we found that when loading a view from a non-child action, if the model was not he RenderModel or an IPublishedContent object, the templates would complain about the incompatibility of the model with the UmbracoTemplatePage class that our layouts inherited. MVC by default uses one of two base classes for your views:

  • WebViewPage: This class does not know about any view model. This means that it can be used as a layout file for any number of views with different models.
  • WebViewPage<TModel>: When you use the @model directive, this is the base page you are using. This is tipically used only on the main view, but not on layouts.

When sharing an Umbraco layout with MVC pages, it is important to understand that UmbracoTemplatePage is equivalent to WebViewPage<RenderModel> (for the sake of argument). Therefore we had made another decision to forbid the use of the UmbracoTemplatePage.

Coming back to the UmbracoMvcCatalyst project, I have provided an alternative base page called UmbracoMvcTemplatePage. By inheriting this class, you will lose access to the UmbracoHelper, and you will lose access to the Model. This class does however support the tracing and preview features of Umbraco.

Losing the UmbracoHelper, you may find simple tasks such as displaying the title of your page a little harder to do. Fortunately, there is another property provided: Content.

Content is a property that you will be able to access from your view which represents the current page. In order to ensure that this property is properly initialised, you will need to use the second method on our static helper: Catalyst.PrepareFilter(GlobalFilterCollection filters).

You can use methods on the content property to fetch simple property values, but you will still lose a lot of helpers from Umbraco. This means that you will likely need to re-write these helpers in an MVC friendly way, or stop sharing layouts between Umbraco and MVC pages.

By using a similar custom library to the one above at work, we were able to re-use the CMS driven features across MVC pages. This included:

  • Navigation
  • Footer Navigation
  • Dynamic Sidebars
  • Disclaimers loaded from the CMS

When rendering an MVC page, the Content property does not become useless. You will get a usable IPublishedContent object, with some limitations:

  • This object will have no parents, siblings or children.
  • It’s name will be based on the name of the action.
  • The url will be based on the current request url.
  • A lot of the properties such as version, id, writerId, etc. will return default or unusable values.
  • Properties are based off the ViewData object.

Having properties based off the ViewData object is the most important and most interesting part of this implementation. A lot of developers place meta data such as the page title and possibly other things such as meta tags into the ViewBag/ViewData for the layout file to use.

When dealing with Umbraco, we tend to use the Umbraco Api to pull these values from the CMS. By marrying the two together, you are able to continue without changing too many things. Here are some things to note:

  • If you have a property called “pageTitle” in the CMS, it is advised that you use the same alias “pageTitle” key when setting the equivalent value in an MVC page.
  • By using the same key, you reduce the need to detect if you are on an MVC page vs an Umbraco page.
  • Since the page will have no parents, the recursive parameter in GetProperty() will have no effect on an MvcContent object.
  • In the case where you need to be able to determine between a real IPublishedContent object and an MvcContent object, you can use the extension method: IsMvcContent().
  • If you find that you are writing long lines of code, or that you require code blocks, we recommend that you opt for creating a child action and doing your pre-processing from there.

Thanks Again!

I would just like to finish off this article by expressing my gratitude to all who visited and commented on my articles, It really helped to give me the motivation to continue writing. I hope that this article and the referenced code will be of use to many of you out there.

I am open to advice and opinions regarding the ReturnNull codebase; feel free to fork and/or alter and re-use the code for your own projects.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s