Scaffolding with Asp.Net MVC 2 and SubSonic 3

Tags: DataBase, .Net, MVC

So I would consider myself one of those Alt.Net guys always looking for a better way of doing things.  I have seen a few good C# guys move to Ruby and once introduced to SubSonic 2.1 with the ActiveRecord pattern and Scaffolding I could see why they would love those types of time saving things baked into the Framework.  Putting my monologue aside one of the things that I have been missing with SubSonic 3.0 is scaffolding.

Why Scaffolding

Any website of any size has some sort of Administration Module.  Why spend more time than necessary building the part of your website that hardly anyone ever sees or uses.  Oh and I'll bet you have never been asked to make a prototype.

MVC2 is so close but is still missing the "Baked In"

With ASP.NET MVC2 and the Html.EditorFor() you hav3 75% of what it takes to get Scaffolding working i.e. a system to create an editor dynamically for you and to top it off you can put attributes on your classes to change the display or validation of the class.

While I was playing with MVC 2 it kind of hit me. I'll bet I can make a single View to handle the Editing/Viewing/Displaying of any Model and put it in the Shared Folder so that it is easily accessible to any Controller. 

While I was at it I could create a Base Class for a Controller that would use SubSonic to persist any POCO class to the DB using the SimpleRepository to give me a Scaffolding affect.

The End Result

So now to create a Table in my DB, an List View of the Items in the Table, a Create New View and an Edit View I only need to add a POCO Class like so

   1:  public class Test
   2:  {
   3:      [HiddenInput]
   4:      public int ID { get; set; }
   5:      public string Name { get; set; }
   6:      public DateTime TestDate { get; set; }
   7:      public Decimal TestDecimal { get; set; }
   8:  }

And Then a Controller Like So

   1:      public class TestController : ScaffoldController<Test>
   2:      {
   3:   
   4:      }

 

Ok ok I know I cheated showing you the End Result but I often don't care about what they did until I decide that I might some time want to do the same thing.

 

How I Got There

 

In order to get the Desired Affect I created the following View

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <%if (this.ViewContext.RouteData.Values["action"].ToString() == "Edit"
 || this.ViewContext.RouteData.Values["action"].ToString() == "Add")
      { %>
    <h2>Editor</h2>
        <%Html.BeginForm(new { action = this.ViewContext.RouteData.Values["action"].ToString() }); %>
        <%=Html.EditorForModel() %>
        <input type="submit" value="Save" />
        <%Html.EndForm();%>
        <%=Html.RouteLink("Back To List", new { action = "Index" })%>
    <%} %>
    <%if (this.ViewContext.RouteData.Values["action"].ToString() == "Display")
      { %>
        <%=Html.DisplayForModel() %>
        
    <%} %>
    <%if (this.ViewContext.RouteData.Values["action"].ToString() == "Index")
      { %>
        <%=Html.RouteLink("Add New", new { action = "Add" })%>
        <br />
        <%=Html.Table(Model as IEnumerable) %>
    <%} %>
    <%if(ViewData.ContainsKey("results")){ %>
        <%=ViewData["results"].ToString() %>
    <%} %>
</asp:Content>

 

and the following Controller

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using SubSonic;
using SubSonic.Repository;
using SubSonic.DataProviders;
 
public class ScaffoldController<T> : Controller where T: class,new()
{
 
    public static SimpleRepository simpleRepo;
 
    public static SimpleRepository Repo
    {
        get
        {
            if (simpleRepo == null)
                simpleRepo = new SimpleRepository(SimpleRepositoryOptions.RunMigrations);
            return simpleRepo;
        }
    }
 
    public virtual ActionResult Edit(object key)
    {
        T mod = Repo.Single<T>(key);
        return View("Scaffold", mod);
    }
 
    [AcceptVerbs(HttpVerbs.Post)]
    public virtual ActionResult Edit(T mod)
    {
        if (this.ModelState.IsValid)
        {
            Repo.Update(mod);
            this.ModelState.Clear();
            ViewData["results"] = "Saved Successfully";
        }
        return View("Scaffold", mod);
    }
 
    public virtual ActionResult Display()
    {
        T mod = new T();
        return View("Scaffold", mod);
    }
 
    public virtual ActionResult Add()
    {
        T mod = new T();
        return View("Scaffold", mod);
    }
 
    [AcceptVerbs(HttpVerbs.Post)]
    public virtual ActionResult Add(T mod)
    {
        if (this.ModelState.IsValid)
        {
            Repo.Add(mod);
            ViewData["results"] = "Saved Successfully";
            ViewContext vc = new ViewContext();
 
            this.RouteData.Values["action"] = "Edit";
            this.ModelState.Clear();
        }
        return View("Scaffold", mod);
    }
 
    public virtual ActionResult Index(int? page, int? perpage)
    {
        var query = Repo.All<T>();
        int num = perpage ?? 20;
        if(page != null)
        {
            query.Skip((page.Value-1)*num);
        }
        query.Take(num);
        return View("Scaffold", query.ToList());
    }
}

If you would like to see the project in action below is a link to the code.  You will need VS 2010 RC to open the file.

4 Comments

  • Kurt Ward said

    Great post! I just finished a project using ASP.NET MVC 1 using Subsonic 3, and certainly had some struggles with certain things. Can't wait to give MVC 2 a try with Subsonic.

  • BrianP said

    This is awesome! I'm coming from Dynamic Data and was looking for a way to do this in MVC. I modified the ScaffoldController class to use Linq to SQL and it works great. Thanks!

  • runxc1 said

    Brian Any way you could send me your mod? I am thinking of starting an MVC Scaffolding project on Codeplex... perhaps I could have a SubSonic/Ling to Sql and then an Entities Framework example and use either the Telerik Open Source Grid or one of the others out there. I am also thinking that it would be nice to create a spark view as well. Anyone else interested?

  • BrianP said

    It's Sent.
    I like the idea. I was thinking the Telerik grid would be nice too. Whatever you go with I would like to suggest that you use Html.DisplayFor when rendering the fields so that the grid display can be customized by using templates. Also it would be nice if it respected some of the System.ComponentModel.DataAnnotations attributes like [ScaffoldColumn(false)] if you don't want to show a column on the grid.

Add a Comment