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

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 |

About Tom

Tom Janssens op LinkedIn

Tom Janssens op twitter

Core bvba RSS

 

Tom Janssens is an independent freelance ICT consultant that has been "into computers" ever since the age of 7.

Typing source code from a book evolved into exploring the limits of coding in procedural, assembly and object-oriented languages.
As he matured in software coding, he started focussing on the problems surrounding software development, and learned that software development is usually about people and interactions first, and about technology second.

Due to his diverse track record he gained insights in a lot of aspects of the software development process. Currently his main focus is on strategic ICT advice, lean product/project development and improving the software development process and architecture.

He avoids ivory-tower-approaches by applying and verifying the applicability of the latest tech buzz in software experiments.

He is also the founder of the following LinkedIn groups:

CQRS Professional
BDD Professional
Asp.Net MVC professional

More info about Tom and his company...


Advertisement

Forget all your SCRUM -, Kanban - and other Agile and Lean certificates

Here is the only true AGILE and LEAN certificate you will ever need:

The Creative Recursive Analysis Process Certificate
(CRAP Certificate for short)

More info can be found at the official CRAP certificate website:
http://bit.ly/CRAPCertificate