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

You can download the full source here using (msys)git:
     https://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:
//     https://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