RSS

Category Archives: ASP.Net MVC

Adding a Lazy Loading Pattern to T4MVC

I added T4MVC to our ASP.NET MVC application yesterday. Great idea – don’t know why it’s taken me so long to get ’round to it? Unfortunately, a number of my unit tests were now failing with a System.NotImplementedException being thrown.

Some of our controllers are currently redundant so I changed the base class’ constructor to make sure no-one called them accidentally…

public abstract class MyRedundantBaseClass
{
  public MyRedundantBaseClass()
  {
    throw new NotImplementedException();
  }
}

Now, none of my tests go anywhere near this class (or any inherited ones) so why was it throwing the exceptions?

The problem is in one of the auto-generated classes in the T4MVC.cs file. The template generates a static field for each non-abstract controllers in your application…

public static class MVC
{
  public static MyApp.Controllers.Account.AccountController Account = new MyApp.Controllers.Account.T4MVC_AccountController();
  // Repeated for each controller...
}

This list of fields also included a number that extended my redundant class. So, as soon as I did anything with the T4MVC framework all these classes got automatically instantiated and now I’m dead in the water.

My solution is to edit the template so that it generates a Lazy Loading version of the T4MVC.cs file. This also makes it a little more efficient as it will only new-up classes when it needs them.

Here’s the old template section (starting from line 73)…

<#foreach (var controller in DefaultArea.GetControllers()) { #>
    public static <#=controller.FullClassName #> <#=controller.Name #> = new <#=controller.FullDerivedClassName #>();
<#} #>

… and here’s what I changed it to…

<#foreach (var controller in DefaultArea.GetControllers()) { #>
 private static <#=controller.FullClassName #> _<#=controller.Name #>;
<#} #>
<#foreach (var controller in DefaultArea.GetControllers()) { #>
 public static <#=controller.FullClassName #> <#=controller.Name #>
 {
  get
  {
   if (_<#=controller.Name #> == null)
     _<#=controller.Name #> = new <#=controller.FullDerivedClassName #>();
     
   return _<#=controller.Name #>;
  }
 }
<#} #>

This now generates the following code in T4MVC.cs…

public static class MVC
{
private static MyApp.Controllers.Account.AccountController _Account;
public static MyApp.Controllers.Account.AccountController Account
{
get
{
if (_Account == null)
_Account = new MyApp.Controllers.Account.T4MVC_AccountController();

return _Account;
}
}

// Repeated for each controller…
}
[/sourcecode}
I still need to add a double locking mechanism for thread safety, but I think this makes a valuable improvement to an already great framework.

Advertisements
 
1 Comment

Posted by on April 6, 2010 in ASP.Net MVC

 

Tags: ,

ASP.NET MVC Html.RadioButton Generates Invalid XHTML

It was pointed out to me by a colleague that the Html.RadioButton(…) extension methods produce invalid XHTML. The output is invalid because, by default, the method will duplicate html “id” attributes. For example, let’s say I have a Colours View Model…

public enum Colours
{
    Red,
    Blue,
    Green
}

public class ColoursViewModel
{
  public Colours ChosenColour
  {
    get;
    set;
  }
}

Then I could create a simple action like this …

public class TestController
{
    public ActionResult SelectColour()
    {
        return View(new ColoursViewModel());
    }
}

… that rendered the following view …

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ColoursViewModel>" %>
<%= Html.RadioButton("ChosenColour", "Red"); %>
<%= Html.RadioButton("ChosenColour", "Green"); %>
<%= Html.RadioButton("ChosenColour", "Blue"); %>

The MVC framework will automatically bind my view model’s “ChosenColour” property to the radio button collection so that I can post the results back like this…

public class TestController
{
    [AcceptVerbs(HttpVerbs.Post)]
    public string SelectColour(ColoursViewModel viewModel)
    {
        return "You chose " + viewModel.ChosenColour.ToString();
    }
}

The problem here is that the generated html looks like this (notice the duplicate id attributes)…

This means that we cannot use the “for” attribute on any labels associated with the individual radio buttons nor can we use javascript to select the radio button when that associated label is clicked.

This is how you fix the problem…

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ColoursViewModel>" %>
<%= Html.RadioButton("ChosenColour", "Red", new { id = "chooseRed" }); %>
<%= Html.RadioButton("ChosenColour", "Green", new { id = "chooseGreen" }); %>
<%= Html.RadioButton("ChosenColour", "Blue", new { id = "chooseBlue" }); %>

… which will generate this html fragment…

<input id="chooseRed" name="ChosenColour" type="radio" value="Red">
<input id="chooseGreen" name="ChosenColour" type="radio" value="Green">
<input id="chooseBlue" name="ChosenColour" type="radio" value="Blue">

The point I’m trying to make here is that the id attribute should be a first class parameter of the RadioButton extension method. I shouldn’t have to override it using the htmlAttributes parameter. This is what the official extension methods look like…

public static string RadioButton(this HtmlHelper htmlHelper, string name, object value);
public static string RadioButton(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes);
public static string RadioButton(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes);
public static string RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked);
public static string RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked, object htmlAttributes);
public static string RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked, IDictionary<string, object> htmlAttributes);

You could consider making a change to the MVC framework libraries to include the id, thus decoupling it from the name. For example..

public static string RadioButton(this HtmlHelper htmlHelper, string name, string id, object value);

So anyway, the take-away from this blog post is “Html.RadioButton(…) will produce invalid XHTML unless you pay attention.”

 
2 Comments

Posted by on February 27, 2010 in ASP.Net MVC

 

Question: When is a non-static class static?

Answer: When it’s a Provider.

OK, quite an obtuse question there so let me explain…

In our ASP.NET MVC application we have a custom Membership Provider and a custom Role Provider. Here’s a (vastly simplified) example of the membership provider…

public class CustomMembershipProvider : MembershipProvider
{
  private SqlConnection _connection;
  private string _userName;
  private string _password;

  public CustomMembershipProvider()
  {
  }

  public override bool ValidateUser(string username, string password)
  {
    _userName = username;
    _password = password;

    bool success = false;

    try
    {
      // Sets the _cn member...
      OpenConnection();

      success = ValidateUser();
    }
    finally
    {
      CloseConnection();
    }

    return success;
  }
}

So, pretty straight forward. Nothing wrong with that, is there?

Well, actually, yes. The problem is that you may write this class with the expectation that it will be treated as a  POIO (Plain Old Instance Object) but ASP.NET has other ideas. One, and only one, of these objects is instantiated (during the first request to you site). The big issue here is that we now have private fields on a singleton class Рnot good.

We ran into problems with SqlDataReaders because the runtime complained about multiple readers on the same connection. We could have implemented MARS, but that still didn’t deal with the underlying problem. Worse still is the possibility of an unauthorised user actually being authorised! You only need user1’s time-slice to end and user2’s to begin midway through that ValidateUser method and you’ve got serious problems.

This is a summarised suggestion of how we solved this problem…

public class CustomMembershipProvider : MembershipProvider
{
  public CustomMembershipProvider()
  {
  }
  public override bool ValidateUser(string username, string password)
  {
    bool success = false;

    using (var cn = OpenConnection())
    {
      var validater = new UserValidater(cn, username, password);

      success = validater.IsValid();
    }

    return success;
  }

  private class UserValidater
  {
    private readonly SqlConnection _connection;
    private readonly string _userName;
    private readonly string _password;

    public UserValidater(SqlConnection cn, string userName, string password)
    {
      _connection = connection;
      _userName = username;
      _password = password;
    }

    public bool IsValid()
    {
      // Logic removed for brevity...
    }
  }
}
 
Leave a comment

Posted by on February 24, 2010 in ASP.Net MVC

 

Tags:

ASP.Net MVC’s Hidden Web.Config File

I’ve just spent some time banging my head against a virtual brick wall built by my Build Server. It has, for some time now, been successfully building and deploying 2 versions (v1 and v2) of the same ‘White Labelled‘ website to an internal testing web server. Recently I edited the definition so that it deleted all v2 files on the testing server before deploying. I did this because MVC Views that the developers had deleted, moved or renamed were remaining in the deployment and causing problems.

So, both versions were absolutely identical except for their web.config files that targeted different databases.

Both sites would allow me to hit the landing and login; but v2 failed to render any view that was strongly typed against a ViewModel. For instance, a view that inherited this would be fine…

System.Web.Mvc.ViewPage

… however, a view that inherited this, would fail…

System.Web.Mvc.ViewPage<MyApp.MyViewModel>

The exception was a System.Web.HttpParseException: “Could not load type ‘MyApp.MyViewModel'”.

Turns out that it was because I wasn’t deploying that innocuous little web.config file that resides in the root View directory. One very important part of this file is to deny direct browsing to the aspx & ascx files – you want any requests to go via your routing model and controllers. The second purpose is to define the base types for your views and how to parse them.

Bottom line: without it your app won’t work.

 
Leave a comment

Posted by on January 18, 2010 in ASP.Net MVC

 

Tags: , ,

ASP.Net MVC Groups


Here’s a fictional example of a problem I’ve been having in ASP.Net MVC

In an MVC application I have 1000 controllers. 2 of these controllers share a partial view so I put it in the ~/Views/Shared/ folder. I then find that I have 499 more pairs of controllers that also share different partial views so I do the same. The problem is that I now have a shared folder cluttered with 500 partial views – each of which is only relevant to 0.2% of my entire application! I also have to give each of these views unique names which becomes unwieldy.

So what’s the solution?

What I’d like to do is group multiple controllers together and give them their own shared area for views. Any controller can also belong to more than one group.

Firstly, we need to extend the ViewEngine that our application is using. This is, primarily, responsible for finding the correct view in our filing system. Out of the box the framework uses ¬†WebFormViewEngine. Our new view engine will inject extra, temporary locations in which to search for views. The information about which locations will be supplied by the current controller. Here’s the code for the new engine…

using System;
using System.Collections.Generic;
using System.Web.Mvc;

namespace MyMvcApp.Web.Mvc
{
  ///
  /// GroupWebFormViewEngine extends WebFormViewEngine and allows Controllers to be
  /// decorated with the MvcGroupAttribute to indicate extra locations Views can be found.
  ///
  public class GroupWebFormViewEngine : WebFormViewEngine
  {
    private string[] _defaultMasterLocationFormats;
    private string[] _defaultPartialViewLocationFormats;
    private string[] _defaultViewLocationFormats;

    public GroupWebFormViewEngine()
      : base()
    {
      // We want to make a note of the base formats to that we can reset them prior to each Find operation
      _defaultMasterLocationFormats = MasterLocationFormats;
      _defaultPartialViewLocationFormats = PartialViewLocationFormats;
      _defaultViewLocationFormats = ViewLocationFormats;
    }

    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
      AddGroupLocations(controllerContext);

      return base.FindView(controllerContext, viewName, masterName, useCache);
    }

    public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
    {
      AddGroupLocations(controllerContext);

      return base.FindPartialView(controllerContext, partialViewName, useCache);
    }

    private void AddGroupLocation(ControllerContext controllerContext)
    {
      ResetAllLocations();

      var type = controllerContext.Controller.GetType();

      var attrs = type.GetCustomAttributes(typeof(MvcGroupAttribute), true);

      Array.ForEach(attrs, attr =>
      {
        AddGroupLocations((attr as MvcGroupAttribute).GroupName);
      });
    }

    private void ResetAllLocations()
    {
      MasterLocationFormats = _defaultMasterLocationFormats;
      PartialViewLocationFormats = _defaultPartialViewLocationsFormats;
      ViewLocationFormats = _defaultViewLocationFormats;
    }

    private void AddGroupLocations(string folderName)
    {
      AddMasterLocationFormats(folderName);
      AddPartialViewLocationFormats(folderName);
      AddViewLocationFormats();
    }

    private void AddMasterLocationFormats(string folderName)
    {
      List<string> currentLocations = new List<string>(MasterLocationFormats);

      currentLocations.Add("~/Views/Shared/" + folderName + "/{0}.master");

      MasterLocationFormats = currentLocations.ToArray();
    }

    private void AddPartialViewLocationFormats(string folderName)
    {
      List<string> currentLocations = new List<string>(PartialViewLocationFormats);

      currentLocations.Add("~/Views/Shared/" + folderName + "/{0}.aspx");
      currentLocations.Add("~/Views/Shared/" + folderName + "/{0}.ascx");

      PartialViewLocationFormats = currentLocations.ToArray();
    }

    private void AddViewLocationFormats()
    {
      ViewLocationFormats = PartialViewLocationFormats;
    }
  }
}

Now we just need our MvcGroupAttribute. This will be used to decorate our controller classes and should apply also to any inherited controllers. It is also possible to have more than one attribute on each controller as it might belong to multiple groups. Here’s the code…

using System;

namespace MyMvcApp
{
  [AttributeUsage(AttributeTarget.Class, AllowMultiple = true, Inherited = true)]
  public class MvcGroupAttribute : Attribute
  {
    ///
    /// Gets or sets the name of a shared group that this controller belongs to.
    ///
    public string GroupName
    {
      get;
      set;
    }
  }
}

Now that we have all our building blocks we can finally use the functionality. Here’s a trivial example…

using System;
using System.Web.Mvc;

namespace MyMvcApp
{
  [MvcGroup(GroupName = "Group1")]
  public class MyFirstController : Controller
  {
    public ActionResult Index()
    {
      return View();
    }
  }

  [MvcGroup(GroupName = "Group1")]
  public class MySecondController : Controller
  {
    public ActionResult Index()
    {
      return View();
    }
  }

  public class MyThirdController : Controller
  {
    public ActionResult Index()
    {
      return View();
    }
  }
}

In the example, above, all 3 controllers have access to views in the ~/Views/Shared/Group1/ directory.

Conclusion

This simple addition to our Mvc applications has been an enormous difference. We now have so much more flexibility to construct our views in a meaningful, clean way. Hopefully you’ll find it useful too?

 
Leave a comment

Posted by on January 7, 2010 in ASP.Net MVC

 

Polymorphic Limitations in Strongly-Typed ASP.Net MVC Views

Strongly-typing your views gives you nice, easy access to what ever view model your controller passes to it. Simple and powerful.

Unfortunately, though, there are some limitations.

  1. Your View Model cannot be abstract.
  2. Your View Model will only get POSTed back as itself.

The first point is fairly simple. Everything works until you POST back your view model e.g.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Update(MyAbstractViewModel model)
{
    // Clever bits go here...
}

Calling this action will result in the framework throwing a MissingMethodException: Cannot create an abstract class.

The second point is a little more complicated. Let’s say you have 3 classes:

public abstract class MyAbstractViewModel
{
}

public class ViewModel1 : MyAbstractViewModel
{
}

public class ViewModel2 : MyAbstractViewModel
{
}

Now you create a simple GET Action that returns a View (strongly-typed to MyAbstractViewModel).

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
    MyAbstractViewModel model;

    if (something)
        model = new ViewModel1();
    else
        model = new ViewModel2();

    return View(model);
}

The view will render successfully. Unfortunately, our original POST Action will not receive either of the concrete classes – only the one the view is explicitly typed against (i.e. MyAbstractViewModel).

These are, obviously, not major issues but it does limit developers’ creative freedom and the most elegant solution may have to be foregone in order to fit the framework.

 
Leave a comment

Posted by on June 9, 2009 in ASP.Net MVC

 

Tags:

Session Availability in ASP.Net MVC

We are in the process of porting one of our Web Forms apps to ASP.Net MVC. This is just something we stumbled upon…

this.Session is null in a Controller’s constructor.
Not a huge issue as you can use a Lazy Loading Pattern to circumvent it. It does raise the question of ‘why?’ though. The MVC (or Routing) framework is responsible for newing-up your Controllers to handle requests. Why couldn’t Session be spun up prior to this?
 
Leave a comment

Posted by on May 21, 2009 in ASP.Net MVC