The Quest for the perfect ASP.Net MVC code : v0.3

by Tom 7. December 2009 20:26

You can download the full source here using (msys)git:
     http://github.com/ToJans/MVCExtensions


// The quest for perfect asp.net MVC code - v0.3
//
// For a while I have been looking for the perfect ASP.Net MVC code.
// This is the cleanest code I have been able to write.
// I would like to challenge everyone to do better !!!
//
// By this I mean creating a better controller/views if possible codewise.
// The focus is not on the layout stuff, but having it might be a plus.
//
// The scope : a very rudimentary Task list (KISS)
//
// You can download the full source here using (msys)git:
//     http://github.com/ToJans/MVCExtensions
//
// You will see it is very easy to alter, just fetch it with git and press F5
//
// Please do let me know what you think about my approach as well,
// and whether you could do better: ToJans@twitter
// Send this link to as much fellow coders as possible, so we can see lots of alternatives
//
// PS: you can also leave a comment @ my website (look at my twitter account for the url)
//
// Edit: this is my third version, and I am still looking for improvements
//
// Some noteworthy facts :
// - In the MVCapp, there only DLL directly referenced is the ViewModel DLL,
//   so the views do NOT reference the controllers anywhere
// - The controller contains only logic & domain model objects => VERY CLEAN Controller
// - The resulting controller action model is mapped to the ViewModel using
//   IMapper.Map<source,ViewModel>(s,vm)
// - The viewmodel should include everything that should be visible on the screen, so not only
//   data but also the actionlinks one can use
// - The actionlinks for the viewpages are defined in the IMapper, and automaticly passed on to
//   the view => you can see/alter the program flow in the mapping definitions
// - Stubbing the controller should be a piece of cake using this code, so you could use this
//   design to easily develop application mockups that are ready to be implemented once the client
//   approves, so first build your viewmodels and views, show it to the client, and upon agreement
//   start development on the controller.... In fact I am going to test this method on my next project
//
// Kind regards,
// Tom Janssens

//
// --------------------------------------------------------------------------------
// Controller :
// --------------------------------------------------------------------------------
namespace Tasks.Core.Controllers
{
    public class HomeController : Controller
    {
        // for the sake of the demo we do not use DI but static instances
        static IRepository<Task> rTask = rTask ?? new FakeRepository<Task>(null);
        static IMapper sMapper = sMapper ?? new Mapper();

        public ActionResult Index()
        {
            return View(sMapper.Map<Task[], VMIndex>(rTask.Find.ToArray()));
        }

        public ActionResult AddNewTask(Task task)
        {
            rTask.SaveOrUpdate(task);
            return this.RedirectToAction(c => c.Index());
        }

        public ActionResult Done(int id)
        {
            var t = rTask.GetById(id);
            t.Done = !t.Done;
            rTask.SaveOrUpdate(t);
            return this.RedirectToAction(c=>c.Index());
        }

        public ActionResult Edit(int id)
        {
            return View(sMapper.Map<Task,VMEdit>(rTask.GetById(id)));
        }

        public ActionResult PostEdit(int id)
        {
            var task = rTask.GetById(id);
            UpdateModel(task);
            rTask.SaveOrUpdate(task);
            return this.RedirectToAction(c => c.Index());
        }

        public ActionResult Delete(int id)
        {
            rTask.Delete(rTask.GetById(id));
            return this.RedirectToAction(c => c.Index());
        }
    }
}

// --------------------------------------------------------------------------------
// Mapping registry :
// --------------------------------------------------------------------------------
am.Mapper.CreateMap<Task, VMIndex.Task>()
   .ForMember(v => v.Name, m => m.MapFrom(t => t.Name))
   .ForMember(v => v.Description, m => m.MapFrom(t => t.Description))
   .ForMember(v => v.AL_Status, m => m.MapFrom(
       t => cHome.AL(t.Done ? "Done" : "Todo", a => a.Done(t.Id))))
   .ForMember(v => v.AL_Edit, m => m.MapFrom(
       t => cHome.AL("Edit", a => a.Edit(t.Id))))
   .ForMember(v => v.AL_Delete, m => m.MapFrom(
       t => cHome.AL("Delete", a => a.Delete(t.Id))));

am.Mapper.CreateMap<Task[], VMIndex>()
   .ForMember(v => v.AllTasks, m => m.MapFrom(t => t))
   .ForMember(v => v.HasNoTasks, m => m.MapFrom(t=>t.Length==0))
   .ForMember(v => v.AL_AddTask, m => m.MapFrom(
       t => cHome.AL("Add new task", c => c.AddNewTask(null))));

am.Mapper.CreateMap<Task, VMEdit>()
   .ForMember(v => v.Name, m => m.MapFrom(t => t.Name))
   .ForMember(v => v.Description, m => m.MapFrom(t => t.Description))
   .ForMember(v => v.AL_CancelEdit, m => m.MapFrom(
       t => cHome.AL("Cancel changes", c => c.Index())))
   .ForMember(v => v.AL_PostEdit, m => m.MapFrom(
       t => cHome.AL("Save changes", c => c.PostEdit(t.Id))));

// --------------------------------------------------------------------------------
// VMIndex.cs - the viewmodel for the index page
// --------------------------------------------------------------------------------
    public class VMIndex
    {
        public class Task
        {
            public string Name;
            public string Description;
            public VMActionLink AL_Status;
            public VMActionLink AL_Edit;
            public VMActionLink AL_Delete;
        }

        public IEnumerable<Task> AllTasks;
        public VMActionLink AL_AddTask;
        public bool HasNoTasks;
    }
// --------------------------------------------------------------------------------
// Index.spark :
// --------------------------------------------------------------------------------
<viewdata model="Tasks.ViewModel.Home.VMIndex" />
<content name="Title">
    Index
</content>

<content name="Main">
<h1>Task list</h1>
<if condition="Model.HasNoTasks">
    No tasks yet.
</if>
<else>
  <table>
    <tr>
      <td>Name</td>
      <td>Name</td>
      <td>Status</td>
      <td>Edit</td>
      <td>Delete</td>
    </tr>
    <tr each="var t in Model.AllTasks">
        <td>${t.Name}</td>
        <td>${t.Description}</td>
        <td><alink a="t.AL_Status" /></td>
        <td><alink a="t.AL_Edit" /> </td>
        <td><alink a="t.AL_Delete" /> </td>
    </tr>
  </table>
</else>
<hr />
<h3>Add a new task</h3>
    <aform a="Model.AL_AddTask">
      <label for="Name">Name :</label><br />
      <input type="text" name="Name" /><br />
      <label for="Description">Description:</label><br />
      <textarea cols="20" rows="2" name="Description" ></textarea><br />
    </aform>
</content>

Bookmark and Share

Tags: , , ,

CodeProject | Development | News | Tom's blog

Comments

12/9/2009 3:41:38 PM #

James Fleming

nice! I've yet to use Spark, but I know w/the T4 templates to lose the magic strings.

James Fleming United States |

2/5/2010 5:32:30 AM #

trackback

The Quest for the perfect ASP.Net MVC code : Let the games begin

The Quest for the perfect ASP.Net MVC code : Let the games begin

Tom's ramblings |

Comments are closed

About me

Tom Janssens op LinkedIn

Tom Janssens op twitter

Core bvba RSS

My name is Tom Janssens and I am the owner of Core bvba, a software and consultancy company.
I am married to Liesbeth and have 2 sons named Quinten and Matisse.
ICT is both my job and passion.
Next to this my other hobby is actively playing music (mostly guitar).

More info about me and my company...

Recent Comments