Showing posts with label asp.net mvc. Show all posts
Showing posts with label asp.net mvc. Show all posts

Monday, 17 March 2014

Compiling ASP.NET MVC Views

If you've ever worked in ASP.NET MVC then you may have noticed that the server-side code in your views (e.g. your Razor logic) is not compiled as part of the build process. By default, views are compiled at runtime which means that any syntax error in your server-side view code may annoyingly only manifest after you've built, deployed and requested for the view which contains the error.

Fortunately, you can instruct the compiler to include your views in the build process by using the project-level boolean property MvcBuildViews. Setting this property to true will ensure your ASP.NET MVC views are also included in the build process and any syntax errors are caught at compile time.

To set this property, open your ASP.NET MVC application's csproj file in your favourite text editor. Locate the MvcBuildViews element and set its inner value to true. If you already have the project open in Visual Studio, the IDE will automatically detect the file change and ask you to reload the project. Once reloaded, you'll have compile time safety for your views.

Image

The only disadvantage to this that I've noticed so far is that your builds take a bit longer to complete, however I've found this negligible compared to finding the error at runtime and having to do a patch deployment to another environment!

Saturday, 23 February 2013

ASP.NET MVC Consolidating "Usings" in Views/Web.config

Something I came across yesterday that you may find useful as an ASP.NET MVC developer. If you find yourself having to include the same set of "usings" across many of your razor views, then you can consolidate them and put them in the Web.config that sits under the Views folder.

The specific config element you're interested in is system.web.webPages.razor/pages/namespaces. It's a case of just creating an entry for each of your commonly used namespaces and then you don't need to worry about adding @using's or fully qualified class names in your views to resolve commonly used types.

...

<pages pageBaseType="System.Web.Mvc.WebViewPage">
  <namespaces>
    <add namespace="System.Web.Mvc" />
    <add namespace="System.Web.Mvc.Ajax" />
    <add namespace="System.Web.Mvc.Html" />
    <add namespace="System.Web.Optimization"/>
    <add namespace="System.Web.Routing" />
    <add namespace="MyMvcApp.Models.ViewModels" />
  </namespaces>
</pages>

...
If you're using the WebForms (ASPX) view engine then the equivalent place to add your global usings is in the same Web.config but within the system.web/pages/namespaces element.

Saturday, 16 February 2013

ASP.NET MVC - Methods in Razor Views (via @helper syntax)

Todays post is about a little-known but very useful feature that was added to the Razor view engine since ASP.NET MVC3. The feature that I'm refering to is the @helper syntax which lets you define reusable methods at the view level. Let's dive straight into an example to show off this feature.

Imagine you have a simple Book model in your ASP.NET MVC project:
public class Book
{
    public string Isbn { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
}
You may then have a controller called BookController with one HTTP GET action called Index. This action may read some books from some repository and pass a list of Book objects to the view. For the sake of brevity, I've hard-coded a couple of Book instances in the snippet below:
public class BookController : Controller
{
    [HttpGet]
    public ActionResult Index()
    {
        var books = new List<Book>();

        books.Add(new Book {
            Isbn = "9780385660075",
            Title = "The Kite Runner",
            Author = "Khaled Hosseini"
        });

        books.Add(new Book {
            Isbn = "9780571219766",
            Title = "Self",
            Author = "Yann Martel"
        });

        return View(books);
    }
}
Initially, your Razor engine-based Index view may look something like:
@using System.Collections.Generic
@using MvcHelperMethods.Models

@model IList<Book>

<h2>Books Index</h2>

@foreach (var book in Model)
{
    <div>
        <span class="bookTitle">@book.Title</span>
        <span class="bookAuthor">by @book.Author</span>
        <span class="bookIsbn">ISBN: @book.Isbn</span>
    </div>
}
Now imagine that you have a new requirement to introduce another view in your application which shows books by individual authors. The requirement states that the books should be displayed exactly how they are on the Index view. In this scenario, you'll need to create another view that accepts a list of Book objects and renders them out. The only difference being that a different controller action would be used which returns books by a specific author. The view code would look very similar. This presents an opportunity to make use of the @helper syntax in Razor.

The @helper syntax lets you define a view-level method that consists of both C# and Razor syntax. This method can be placed in a specific view or outside of a specific view so that it can be reused across multiple views. We'll use the latter option and define an @helper method called RenderBook which accepts a single parameter of type Book and renders the book out using a mixture of Razor and html syntax.

To accomplish this in your ASP.NET MVC solution, ensure you have the App_Code folder. In Visual Studio 2012, you can add this folder by right-clicking your project then going to Add --> Add ASP.NET Folder --> App_Code. In your App_Code folder, you will now create a new MVC Razor View Page (a .cshtml file). Right click the App_Code folder and go to Add -> New Item. Select the option called "MVC View Page (Razor)", name your view page something sensible - like "ViewHelperMethods.cshtml" then click Add. If there is some pre-defined content in the new view, just remove it all. Now you can write your reusable @helper view method called RenderBook in the ViewHelperMethods.cshtml file:

@using MvcHelperMethods.Models

@helper RenderBook(Book book)
{
    <div>
        <span class="bookTitle">@book.Title</span>
        <span class="bookAuthor">by @book.Author</span>
        <span class="bookIsbn">ISBN: @book.Isbn</span>
    </div>
}
As you can see from the syntax, it's quite similar to the syntax of a standard C# method except you don't specify a return type and access modifier, instead, you use the keyword @helper. You can now refactor your original Index view so that it looks like:
@using System.Collections.Generic
@using MvcHelperMethods.Models

@model IList<Book>

<h2>Books Index</h2>

@foreach (var book in Model)
{
    @ViewHelperMethods.RenderBook(book)
}
Notice that we're now calling our reusable RenderBook method. You can now call the same RenderBook method from your other views, in our case, the view that displays books by specific authors. In summary, if you're working on a fairly large ASP.NET MVC application with many views, then this is a very useful feature to ensure your views are more maintainable.

Update: As pointed out by a reader of this post (on Google+), you also have the option to create a partial view for the Book model and reuse that. The example I gave above was quite simplistic and was aimed at introducing the reader to the @helper feature. I believe the advantage that helper methods give is that you can easily pass them additional parameters at the view level and those parameters can then control exactly how the smaller view is rendered out.

Tuesday, 22 May 2012

MVC Model Binding to List of Complex Objects

Recently, I had a requirement to create a form-based partial view in an MVC3 application that permitted a user to select multiple options using standard HTML checkboxes. What this essentially meant was that I needed MVC to automatically bind a complex list of objects to the argument of an action method.

After doing some reading on this, I found that the DefaultModelBinder supports this as long as the form fields are named in such a way that the model can distinguish one complex object from another - this can be achieved by using a unique index when creating the form. The example below shows exactly how this can be done - I'm using MVC3 with the Razor view engine.

Imagine we need to display a list of options to the user, the user can select multiple options from the list and then post the form back to one of your action methods on the server side. In this scenario, we're going to display a list of planes to the user, the user can select their favourite plane(s) and then click a submit button. We'll start by defining our model - a "complex" but self explanatory class called PlaneModel.

public class PlaneModel
{
  public string Manufacturer { get; set; }
  public string Model { get; set; }
  public bool IsFavourite { get; set; }

  public override string ToString()
  {
    return string.Format("{0} - {1}", Manufacturer, Model);
  }
}

For the sake of brevity, I won't use a partial view (but the method should be the same if you want to use a partial view in your case). We'll create a new controller called PlaneController, with one initial action method "Index". In this action method, we'll new-up some instances of PlaneModel, store them in a list-based collection and then pass this collection as a model to a strongly-typed Index view. The Index action method would therefore look like:

[HttpGet]
public ActionResult Index()
{
  var planes = new List<PlaneModel>(); //Model

  planes.Add(new PlaneModel { 
      Manufacturer = "Cessna"
      Model = "C208B Grand Caravan" });
  planes.Add(new PlaneModel { 
      Manufacturer = "Douglas"
      Model = "DC-3" });
  planes.Add(new PlaneModel { 
      Manufacturer = "Piper"
      Model = "J-3 Cub" });
  planes.Add(new PlaneModel { 
      Manufacturer = "Mooney"
      Model = "M20J" });
            
  return View(planes);
}

Notice that the action method maps to our HTTP GET request. So, whilst we're still in our controller, we'll write the POST action. The key thing to remember here is that our post action will accept a list of PlaneModel objects.

[HttpPost]
public ActionResult ProcessFavouritePlanes(List<PlaneModel> model)
{
  foreach (var planeModel in model)
  {
    if (planeModel.IsFavourite)
      Debug.WriteLine("Favourited: {0}", planeModel);
    else
      Debug.WriteLine("Not favourited: {0}", planeModel);
  }
  return View(model);
}

So, all I'm doing in the POST action is iterating through the planes in the model (which will be passed back from the view) - and hopefully the IsFavourite property should have been bound to the correct values that the user selects using checkboxes.

Now onto the important part - the creation of our view. Create a strongly typed Index view (i.e., a generic list of type PlaneModel). If you're using Visual Studio as your IDE, you can right-click within your Index action method and select the option "Add View" - this should bring up a modal dialog. Leave the view name as "Index", check the "Create a strongly-typed view" option and type:

List<PlaneModel>

in the "Model class" text box (note that you will probably need to prefix the PlaneModel class name with its fully qualified namespace as the generic type parameter to List - if you don't do this you'll get a runtime error when navigating to the Index view). You can now click "Add" and the view will get created under the conventional folder structure.

The view logic will be a simple mixture of standard HTML and C# in Razor syntax:

@model List<PlaneModel>
Please select your favourite plane(s):<br />
@using (Html.BeginForm("ProcessFavouritePlanes"
                       "Plane"
                       FormMethod.Post))
{
  for (int i = 0; i < Model.Count; i++)
  {
    @Html.CheckBoxFor(m => m[i].IsFavourite)
    @Model[i].ToString() 
    @Html.HiddenFor(m => m[i].Manufacturer)
    @Html.HiddenFor(m => m[i].Model)
  }
  <input type="submit" value="Go!" />
}

Notice that we're iterating through each PlaneModel object using a C# for-loop. This allows us to use the incrementing index and display each option from the model. Also note the use of the hidden fields for the Manufacturer and Model properties - these are here to ensure that they're passed back to the DefaultModelBinder on the server side - taking these two lines out will mean that we'll get PlaneModel objects with blank values for those two properties when the form is posted to the POST action. You should now be able to test if this is all working by hitting a breakpoint on the POST action, running the application and selecting some options. You'll find that the model binder will automatically bind the selected checkboxes and update the model passed into the action.

To understand why this works, we can take a look at the rendered HTML sent back to the client for our form:

<form action="/Plane/ProcessFavouritePlanes" method="post">
  <input name="[0].IsFavourite" type="checkbox" value="true" />
  <input name="[0].IsFavourite" type="hidden" value="false" />
  Cessna - C208B Grand Caravan 
  <input name="[0].Manufacturer" type="hidden" value="Cessna" />
  <input name="[0].Model" type="hidden" value="C208B Grand Caravan" />
  <br />
  <input name="[1].IsFavourite" type="checkbox" value="true" />
  <input name="[1].IsFavourite" type="hidden" value="false" />
  Douglas - DC-3 
  <input name="[1].Manufacturer" type="hidden" value="Douglas" />
  <input name="[1].Model" type="hidden" value="DC-3" />
  <br />
  <input name="[2].IsFavourite" type="checkbox" value="true" />
  <input name="[2].IsFavourite" type="hidden" value="false" />
  Piper - J-3 Cub 
  <input name="[2].Manufacturer" type="hidden" value="Piper" />
  <input name="[2].Model" type="hidden" value="J-3 Cub" />
  <br />
  <input name="[3].IsFavourite" type="checkbox" value="true" /> 
  <input name="[3].IsFavourite" type="hidden" value="false" />
  Mooney - M20J 
  <input name="[3].Manufacturer" type="hidden" value="Mooney" />
  <input name="[3].Model" type="hidden" value="M20J" />
  <br />
  <input type="submit" value="Go!" />
</form>

Notice how the framework added index prefixes to the name attributes of the input elements. The use of this index-based naming convention for the input elements allows the DefaultModelBinder in MVC to distinguish between each complex object - and therefore seamlessly create a correct representation of our model that is passed to the POST action - very neat!

Wednesday, 11 April 2012

ASP.NET MVC3 Attributes

I am currently working on a project where we're porting an existing SharePoint 2010 site to ASP.NET MVC3. Coming from an ASP.NET Web Forms background, this is an interesting experience. ASP.NET MVC is quite different from the Web Forms model, with one of the biggest differences being its emphasis on "convention over configuration".

Having played with MVC2 in the past, the work on this project has been smooth sailing. I'm particularly enjoying the level of control that the MVC development model provides over the resulting HTML sent back to a users browser. The bloated HTML from the Web Forms model (quickly pointing the finger at the ViewState hidden field) is something I don't have to painfully scroll over when inspecting the generated HTML.

I particularly like how MVC makes use of attributes on action methods, things like the AuthorizeAttribute are a nice and clean way of executing repeated functionality without it really getting in the way of your usual controller or action level logic.

The main purpose of this post is therefore just for me to continuously document the useful attributes I come across. My intention is to update the table below whilst working on the project mentioned above.

Attribute Name Notes
System.Web.Mvc.AuthorizeAttribute Applied on actions that require the user to be authenticated. If the user is not authenticated, they are automaticallly redirected to the login page. Can also be used at the controlller (class) level - which applies it on all actions within the controller.
System.Web.Mvc.ChildActionOnlyAttribute Applied on actions that shouldn't be invokable directly through the browser. Use this when the action returns inline HTML markup e.g. partial views. Can only be called via Action or RenderAction HTML extension methods.
System.Web.Mvc.NonActionAttribute Use this when you don't want the MVC framework to treat a method in your controller class as an action. By default, the framework will treat all public methods in a controller as an action method.
System.Web.Mvc.OutputCacheAttribute Used to cache the output of an action. Has a number of useful named parameters, one of which is "Duration" that lets you specify the number of seconds to cache the output for.