LibrarySites.Banner

About MVC Helpers with the Sitecore ASP.NET CMS

This blog post contains information about helper classes available to ASP.NET MVC applications. Much of the information in this post is not specific to the Sitecore ASP.NET web Content Management System (CMS), but provides some context for another post I intend to write about using MVC with Sitecore. For more information about using MVC with Sitecore, see the blog post Posts about Using MVC with the Sitecore ASP.NET CMS. Before going into this, I want to make it clear that I am not an expert on ASP.NET MVC; I am learning MVC while learning the Sitecore MVC implementation thereof. Therefore any guidance I provide might be functional (or might not!), but might also not be considered best practice (a topic generally consider to be subjective anyway). To be totally honest, I'm always still learning C# and .NET.

While routing and controllers are fundamental in ASP.NET MVC, to me, the most important components of an MVC application are the models and the views. To oversimplify, the model is an object that contains data for the view to render, where the view works something like a Web Form (.aspx file) or Web User Control (.ascx file) in ASP.NET Web Forms.

Unlike ASP.NET Web Forms, in ASP.NET MVC, views have no code-behind. You can intersperse code (specifically, code to manage presentation) and markup in a view, but this does not support strong separation of concerns and could become unwieldy and difficult to maintain. Additionally, code in a view is not inherently reusable except in the context of that view, which you can apply to any object based on the model that view supports. Most importantly, code in a view should only affect presentation and never implement any business logic.

So where do you put code that generates markup? You could consider the model to be a view model rather than a domain model and put that code in the model, but this approach does not clearly separate data and presentation concerns. The blog post Sample Base Class for MVC View Models in the Sitecore ASP.NET CMS describes that approach, which I must reconsider. Additionally, methods in a model are available only to that model and any models that derive from that model, and you may want to derive your models from other classes that do not contain that code. Alternatively, you could put that code in a view or base class for views, but like models, you may want (at least some of) your views to derive from other classes that do not contain that code.

ASP.NET MVC resolves this issue by providing three helpers classes:

  • System.Web.Mvc.AjaxHelper: According to MSDN, the AjaxHelper class "Represents support for rendering HTML in AJAX scenarios within a view." It looks like the most important method in the AjaxHelper class is JavaScriptStringEncode().
  • System.Web.Mvc.HtmlHelper: According to MSDN, the HtmlHelper class "Represents support for rendering HTML controls in a view", including a variety of methods to generate elements used in HTML forms. The HtmlHelper class may be the most important helper for many MVC applications.
  • System.Web.Mvc.UrlHelper: Accoding to MSDN, the UrlHelper class "Contains methods to build URLs for ASP.NET MVC within an application."

The Ajax, Html, and Url properties of the default base classes for views (System.Web.Mvc.WebViewPage and System.Web.Mvc.WebViewPage<TModel>) expose instances of these types, which the InitHelpers() methods of these classes instantiate. Various methods invoke the InitHelpers() methods, so I think you can count on those properties having values before you access them.

I strongly doubt the following details are important to most readers, but I found it interesting that the InitHelpers() method of the System.Web.Mvc.WebViewPage class from which views inherit by default sets the Ajax and Html (technically Ajax<TModel> and Html<TModel>) properties of that class to instances of AjaxHelper<object> and HtmlHelper<object>, where AjaxHelper<TModel> and HtmlHelper<TModel> derive from AjaxHelper and HtmlHelper. There is no UrlHelper<TModel> type, so the Url property is the same for both the System.Web.Mvc.WebViewPage class and the System.Web.Mvc.WebViewPage<TModel>class, which derives from System.Web.Mvc.WebViewPage.

What is probably important is that you can write extension methods for these existing helper classes, you can write your own helper classes, and you can override the default base classes for views to expose instances of your helper classes. I think common practice might be to extend the existing helpers for things that are relevant to AJAX (or AJWJ for Asynchronous JavaScript with JSON?), HTML, and URLs. For a little bit of information about writing extension methods, see the blog post Extend Classes Provided by the Sitecore ASP.NET CMS; the technique described applies to any instance class. 

You might implement your own helper classes for things unrelated to these goals, such as Sitecore-specific code, or if your methods need to maintain state, where unlike static extension methods, instance classes can contain non-static fields. In fact, the Sitecore() method in the Sitecore.Mvc.HtmlHelperExtensions class, which extends the System.Web.Mvc.HtmlHelper class, returns an instance of the Sitecore.Mvc.Helpers.SitecoreHelper class, which contains the methods you call to work with Sitecore in an MVC view. The SitecoreHelper class is not static and not an extension, but an instance class.

In a view, you can call methods in the System.Web.Mvc.HtmlHelper class using constructs such as:

@Html.Sitecore().Field("FieldNameOrID")

Your first concern on seeing this might be that if you need to render a number of fields, this creates that number of Sitecore.Mvc.Helpers.SitecoreHelper helper objects when you should only need one, which would put a minor load on object instantiation but more importantly, garbage collection, and could lead to memory fragmentation. But the Sitecore() method appears to contain some logic that creates a SItecoreHelper object on the first invocation of this method for a thread, and then associates that object with the thread. This works something like the singleton pattern, but on a thread basis instead of an application basis.

Your second concern may be that you need to add a @using directive for the Sitecore.Mvc namespace to any view that uses the Sitecore() method:

@using Sitecore.Mvc

To avoid this, you can add an <add> element to the /configuration/system.web.webPages.razor/pages/namespaces section of the /Views/Web.config file (where the /Views subdirectory contains all of your views):

<add namespace="Sitecore.Mvc" />

Your third concern might be "but I don’t like that @Html.Sitecore() syntax", and I agree with you. I don't think there's anything I can do about the @ or the missing statement terminator (which shows up in your markup if you add it), but if you want, you can override the base classes for views with code such as the following:

namespace Sitecore.Sharedsource.Web.Mvc
{
  using Sitecore.Mvc;
  
  using SC = Sitecore;
  
  public abstract class WebViewPage : System.Web.Mvc.WebViewPage
  {
    public SC.Mvc.Helpers.SitecoreHelper SitecoreHelper { get; private set; }
  
    public override void InitHelpers()
    {
      base.InitHelpers();
      this.SitecoreHelper = this.Html.Sitecore();
    }
  }
  
  public abstract class WebViewPage<TModel> : System.Web.Mvc.WebViewPage<TModel>
  {
    public SC.Mvc.Helpers.SitecoreHelper SitecoreHelper { get; private set; }
  
    public override void InitHelpers()
    {
      base.InitHelpers();
      this.SitecoreHelper = this.Html.Sitecore();
    }
  }
}

Note:

  • I named the property SitecoreHelper instead of what I would have preferred (Sitecore) to avoid an intellisense annoyance due to the conflict between the property name and the Sitecore namespace, but you can name such a property whatever you want. Hopefully the property name matching the class name does not cause confusion.
  • Sitecore.Sharedsource.Web.Mvc.WebViewPage<TModel> cannot inherit from Sitecore.Sharedsource.Web.Mvc.WebViewPage because then it would not contain anything in System.Web.Mvc.WebViewPage<TModel> but not in System.Web.Mvc.WebViewPage.
  • This uses the Sitecore() method of the Sitecore.Mvc.HtmlHelperExtensions class to get a SitecoreHelper instance rather than constructing a SitecoreHelper in case it's important to use the object associated with the thread (for instance, if that class creates a stack or some other type of list where order is important, using two instances of that class could cause issues).

To cause a view to derive from this first of Sitecore.Sharedsource.Web.Mvc.WebViewPage class, you can use the @inherits directive in the view:

@inherits Sitecore.Sharedsource.Web.Mvc.WebViewPage

To cause a strongly-typed view to derive from the Sitecore.Sharedsource.Web.Mvc.WebViewPage, you can use the @inherits directive, but you have to specify the type of the model (System.String in this example, although I consider that to be a terrible model and use it only for demonstration purposes without the need to introduce another class):

@inherits Sitecore.Sharedsource.Web.Mvc.WebViewPage<System.String>

If you want all of your views to inherit from these classes, you can update the pageBaseType attribute of the /configuration/system.web.webPages.razor/pages element in the /Views/Web.config file:

<pages pageBaseType="Sitecore.Sharedsource.Web.Mvc.WebViewPage">

Now the syntax in your view is a little more straightforward, you don't need any extra @inherits or @using directives, and you don't have to concern yourself with how Sitecore manages the SitecoreHelper object instantiation:

@SitecoreHelper.Field("FieldNameOrID")
  • Hi All , I am facing the similar issue in my sitecore website and have tried couple of solutions but no luck , here is my error , when publish my Dev profile on local filesystem it throws this compiler error.

    Compilation Error

    Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.

    Compiler Error Message: CS1061: 'Sitecore.Mvc.Helpers.SitecoreHelper' does not contain a definition for 'VisitorIdentification' and no extension method 'VisitorIdentification' accepting a first argument of type 'Sitecore.Mvc.Helpers.SitecoreHelper' could be found (are you missing a using directive or an assembly reference?)

    Source Error:

    Line 17:     @* TODO: Load this only on pages that require FlexSlider *@

    Line 18:     <link rel="stylesheet" href="/styles/flexslider/flexslider.css" type="text/css" media="screen">

    Line 19:     @Html.Sitecore().VisitorIdentification()

    Line 20:

    Line 21:     <script src="code.jquery.com/.../script>

    Source File: c:\inetpub\wwwroot\<websitename>\Website\Views\Website\Layouts\Default.cshtml    Line: 19

    Microsoft (R) Visual C# Compiler version 4.6.1087.0

    for Microsoft (R) .NET Framework 4.5

    c:\inetpub\wwwroot\<Websitename>\Website\Views\Website\Layouts\Default.cshtml(19,23): error CS1061: 'Sitecore.Mvc.Helpers.SitecoreHelper' does not contain a definition for 'VisitorIdentification' and no extension method 'VisitorIdentification' accepting a first argument of type 'Sitecore.Mvc.Helpers.SitecoreHelper' could be found (are you missing a using directive or an assembly reference?)

  • Hey ,

    I'm having similar issues, but found out that you'll need to define the following using in the layout template:

    @using Sitecore.Mvc.Analytics.Extensions