Category Archives: MVC2

Populating common view model attributes

In many ASP.Net MVC based applications, we often find ourselves needing to include the same information in every page.  Such information typically includes details of the current user, maybe environmental values and running totals like the number of items in a shopping basket.

In order to ensure these values are always available in views, base your view models on a common base class, and populate these base values using an action filter.

public class ViewModelBase
{
   public string UserName { get; set; }
   public List<UserFavorite> UserFavorites { get; set; }
   public ShoppingBasket ShoppingBasket { get; set; }
}

Then, create an action filter to populate this, if it has not already been populated by the action method on which it has been applied.

// Populates View Model base properties, if they are not already populated by the controller
class ViewModelAttribute : ActionFilterAttribute
{
   public override void OnActionExecuted(ActionExecutedContext filterContext)
   {
   ViewModelBase viewModel;

   if (filterContext.Controller.ViewData.Model == null)
   {
      viewModel = new ViewModelBase();
      filterContext.Controller.ViewData.Model = viewModel;
   }
   else
   {
      viewModel = filterContext.Controller.ViewData.Model as ViewModelBase;
   }

   if (viewModel != null)
   {
      viewModel.UserName = lookupUserName();
      viewModel.UserFavorites = GetUserFavorites();
      viewModel.ShoppingBasket = GetShoppingBasket();
   }

   base.OnActionExecuted(filterContext);
   }
}

The code first checks for a null model and assigns a base view model if none exists.  It then checks that if a model does already exist, that it is of type ViewModelBase.  If so, the appropriate values are initialized.

Then, on any action method that required this – or (in my case) at the controller level of a base controller class, add the attribute:

[ViewModel]
public abstract class ControllerBase : Controller
{
 /// Controller base

}

Rendering views to strings

Rendering a view or a partial to a string is a common problem, especially when you want to re-use your code and markup in a PDF or Email.  There are a number of semi-successful methods for achieving this across the internet.  The techniques outlined below are built on the excellent post from Kevin Craft at http://craftycodeblog.com/2010/05/15/asp-net-mvc-render-partial-view-to-string/ this has the advantage of being simple, straight forward and fully support embedded Html helpers.

Render Partial To String & Render Form To String

Define the following within a Controller base class:

public class BaseController : Controller
{
   protected string RenderPartialToString(string viewName, object model)
   {
      if (string.IsNullOrEmpty(viewName))
         viewName = ControllerContext.RouteData.GetRequiredString("action");

      ViewData.Model = model;
      using (StringWriter sw = new StringWriter())
      {
         ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
         validateViewResult(viewResult, viewName);
         ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
         viewResult.View.Render(viewContext, sw);
         return sw.GetStringBuilder().ToString();
      }
   }

   protected string RenderViewToString(string viewName, object model, string masterName)
   {
      if (string.IsNullOrEmpty(viewName))
      viewName = ControllerContext.RouteData.GetRequiredString("action");
      ViewData.Model = model;
      using (StringWriter sw = new StringWriter())
      {
         ViewEngineResult viewResult = ViewEngines.Engines.FindView(ControllerContext, viewName, masterName);
         validateViewResult(viewResult, viewName);
         ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
         viewResult.View.Render(viewContext, sw);
         return sw.GetStringBuilder().ToString();
      }
   }

   private static void validateViewResult(ViewEngineResult viewResult, string viewName)
   {
      if (viewResult.View != null)
      return;
      // wasn't found - construct error message
      StringBuilder locationsText = new StringBuilder();
      foreach (string location in viewResult.SearchedLocations)
      {
         locationsText.AppendLine();
         locationsText.Append(location);
      }
      throw new InvalidOperationException(String.Format("View {0} not found. Locations Searched: {1}", viewName, locationsText));
   }
}

Then, within your controller action methods you can use something similar to this:

string partial = RenderPartialToString("TestPartial", "This is my test partial");
string viewString = this. RenderViewToString("", null, "");

Using MVC RenderAction within a Webform

As pointed out in the previous post (<here>) it is possible to call the MVC RenderPartial from within a webform.  This assumes that your webform has all the information it needs to populate the required model.  Where this information is not available, a good alternative is to use RenderAction from within the webform:

Add the following to the MvcUtility class described in the previous post:

public static void RenderAction(string controllerName, string actionName, object routeValues)
{
   RenderPartial("RenderAction", new RenderActionViewModel() { ControllerName = controllerName, ActionName = actionName, RouteValues = routeValues });
}

Create the viewModel to support the data we want to pass:

public class RenderActionViewModel
{
   public string ControllerName { get; set; }
   public string ActionName { get; set; }
   public object RouteValues { get; set; }
}

And, create a partial view to make the RenderAction request (I put mine in the Shared directory)

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<RenderActionViewModel>" %>
<%Html.RenderAction(Model.ActionName, Model.ControllerName, Model.RouteValues); %>

Then, all that is needed is to call the MvcUtility.RenderAction from within a webform, passing it the name of the controller, action and and additional parameters that your action method requires.  Examples:

<% PartialViewHelper.RenderAction("myController", "myAction", new { param = Value }); %>
...
<% PartialViewHelper.RenderAction("Blogs", "List", new { page = 1, order = "date", comments = "off" }); %>

This is a great way of adding MVC functionality to a Webforms environment.

Using MVC RenderPartial within a webform

I you are lucky, like me, you get to work on both MVC and Webforms projects, often simultaneously, in mixed environment projects.

One question that commonly arises – how to render an MVC partial view within a webform.  A little study of the MVC source code, and it reveals that its relatively easy, as long as you are able to fake the necessary context elements.

Step 1. Create a dummy controller from which we can create the controller context.  This is simply an empty class that inherits from Controller.

// create a dummy controller
public class DummyController : Controller
{
}

Step 2. Create a static class MvcUtility that constructs the context and call the render method.

public static class MvcUtility
{
   public static void RenderPartial(string partialViewName, object model)
   {
      // Get the HttpContext
      HttpContextBase httpContextBase = new HttpContextWrapper(HttpContext.Current);
      // Build the route data, pointing to the dummy controller
      RouteData routeData = new RouteData();
      routeData.Values.Add(&quot;controller&quot;, typeof(DummyController).Name);
      // Create the controller context
      ControllerContext controllerContext = new ControllerContext(new RequestContext(httpContextBase, routeData), new DummyController());
      // Find the partial view
      IView view = FindPartialView(controllerContext, partialViewName);
      // create the view context and pass in the model
      ViewContext viewContext = new ViewContext(controllerContext, view, new ViewDataDictionary { Model = model }, new TempDataDictionary(), httpContextBase.Response.Output);
      // finally, render the view
      view.Render(viewContext, httpContextBase.Response.Output);
   }

   private static IView FindPartialView(ControllerContext controllerContext, string partialViewName)
   {
      // try to find the partial view
      ViewEngineResult result = ViewEngines.Engines.FindPartialView(controllerContext, partialViewName);
      if (result.View != null)
      {
         return result.View;
      }
      // wasn't found - construct error message
      StringBuilder locationsText = new StringBuilder();
      foreach (string location in result.SearchedLocations)
      {
         locationsText.AppendLine();
         locationsText.Append(location);
      }
      throw new InvalidOperationException(String.Format(&quot;Partial view {0} not found. Locations Searched: {1},  partialViewName, locationsText));
   }
}

This creates the necessary environment – creating the HttpContext, route data, controller context, finds the view and renders it.  The second routine, FindPartialView uses the ViewEngines to find the partial view and throws an appropriate exception if it can’t find the requested partial.

Finally, within my webform, I create a public property called Model in the code behind and include the following:

<% MvcUtility.RenderPartial("TestPartial", Model); %>

Thats all there is to it…

There is only on drawback to this approach, your code behind needs to populate the model and therefore needs all the model creation information in order to call this.  The next post Using MVC RenderAction within a Webform addresses this… Continue reading

Switching to Https and back to Http in ASP.NET MVC

Within ASP.Net MVC, there is a handy RequireHttpsAttribute that you can use to automatically direct an incoming action request to go through HTTPS.  Typically this would be used for a login or checkout process:

[RequireHttps]
public Actionresult LogIn()
{
...
}

Unfortunately, once switched over to HTTPS, all subsequent requests will also be sent via HTTPS as there is no build in mechanism to return the user to HTTP.

In order to resolve this, I have written a new version of the RequireHttpsAttribute that switches the user back to Http when required.

public class RequireHttpsAttribute : System.Web.Mvc.RequireHttpsAttribute
{
   public bool RequireSecure = false;
   public override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
   {
       if (RequireSecure)
       {
         // default RequireHttps functionality
         base.OnAuthorization(filterContext);
       }
       else
       {
         // non secure requested
         if (filterContext.HttpContext.Request.IsSecureConnection)
         {
             HandleNonHttpRequest(filterContext);
         }
       }
    }

   protected virtual void HandleNonHttpRequest(AuthorizationContext filterContext)
   {
      if (String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
      {
         // redirect to HTTP version of page
         string url = "http://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
         filterContext.Result = new RedirectResult(url);
      }
   }
}

This action filter inherits from the standard RequireHttpsAttribute and overrides the OnAuthorization method.  It also has a parameter ‘RequireSecure’ that is set when applying the filter to indicate whether it should switch to secure (HTTPS) or not.

In order to switch to HTTPS, we then use

[RequireHttps(RequireSecure = True)]
public Actionresult LogIn()
{
...
}

And then set Secure = False either on actions that require it, or at the controller level, or in my case I set it on my base controller class so that it is applied to every action that has not been specifically decorated set to be via HTTPS.

[ActionFilters.RequireHttps(RequireSecure = false)]
public abstract class ControllerBase : Controller
{
...
}

Am I in an MVC or Webforms environment?

I’m currently working on an interesting project which is a combination of legacy code built using Asp.net webforms, as well as a whole bunch of new functionality developed using Asp.net MVC.  Due to the sheer volume of code and functionality within the existing system there is simply no possibility of rewriting all the existing forms and underlying code to use the MVC, however we are using MVC wherever possible to build new functionality and extend the system.

This leads to some interesting challenges, in particular within shared areas of code that need to work differently when the user is within a webforms part of the solution, compared to when the user is in a MVC based page.

It was therefore necessary to try and figure out if a particular web request cycle is part of MVC or not.  Rather than write some complex set of routines (and I tried to develop a few) I came up with a remarkably simple mechanism – ‘ask’ the MVC routing engine whether it is able to route the current web request.  If it can route the request to an MVC controller then MVC is being used, otherwise we can assume that its a webforms request.

// To determine if its an MVC Request, look at for a 'controller' value in the route data for this request
public bool IsMvcRequest(HttpContext context)
{
RouteData routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(context));
if (routeData != null &amp;amp;&amp;amp; routeData.Values["Controller"] != null)
return true;
else
return false;
}

The reason that this works within the webforms environment, is that the Routes class is created as public within the global.asax which is executed for all requests, not just mvc.

Enjoy