Story Details for articles

Golf Tracker - Ep. 7 - Code Templates - Part 1

kahanu
Author:
Version:
Views:
3714
Date Posted:
8/24/2011 7:50:25 PM
Date Updated:
8/24/2011 7:50:25 PM
Rating:
0/0 votes
Framework:
ASP.NET MVC 2
Platform:
Windows
Programming Language:
C#
Technologies:
T4 Templates
Tags:
golf, golf tracker, t4 controllers
Demo site:
Home Page:
Share:

Golf Tracker - Code Templates - Part 1

T4 Code Templates

T4 templates are bits of code that help you generate custom code.  Visual Studio uses T4 templates to generate the default code for controllers and views.

Here's the controller.tt T4 Template that generates the code for your default controllers.  It is buried way down in the bowels of the Visual Studio folder.

controller.tt location

The T4 templates for creating the views are in the AddView folder.

T4 templates for views

So why is it necessary to know anything about T4 templates?

You can customize the T4 templates that generate the controller and view code so all the code that is generated every time you either select "Add Controller" or "Add View" contains boiler plate code that works with your application.  No two developers construct their controllers the same way, and good developers follow specific conventions and they usually have their own standards and methodologies when developing applications.

By creating custom T4 templates for the controllers and views, all your code will be exactly the way you want it.  The time saved when generating new controllers and views will be worth the time spent customizing the T4 templates.

T4 Template Preparation

So what is necessary to create a custom T4 controller template?  For MVC 2, the first thing I need to do is copy the CodeTemplates folder that Visual Studio uses into my MVC application.

code templates folder

Inside the CodeTemplates folder are two folders that contain the T4 templates for the controller and views.

code templates folder opened

The reason I need to do this, is because Visual Studio makes a decision when it's going to search for the T4 templates.  The first thing it does is check inside the MVC application for the existence of the CodeTemplates folder.  If it exists, then it will use that folder and the T4 templates in the AddController and AddViews folders.  This is where my custom templates will be.

If the CodeTemplates folder is not in my MVC application, then it will use the default templates.

Once I copy the CodeTemplates folder into my MVC application it will look like this.

code templates copied to project

In order for Visual Studio to recognize them they need to be included in the project.  So I right-click on the CodeTemplates folder and select "Include in project".

include code templates in project

Once this folder is included in to the project, it will look like this.

code templates included in project

Now the thing I'll need to do is remove all the code files, whether they are C# or Visual Basic files.  If I select the controller.tt file and view it's properties, I'll see a value in the Custom Tool property.

custom tool property

I'll need to delete this property value from all the Custom Tool properties for the controller and view templates.  The easiest way to do this is to select all the .tt files and clear the Custom Tool property values.

custom tool property value deleted

Once all the code files are gone, I am now ready to customize the templates.

Creating Customize Controllers

The first thing I should do as a practice is create an entire controller with all the basic CRUD methods written and working. Once a controller is completely built and working, then I can use it as a basis for modifying the T4 template.

In order to customize the controller.tt file, I open it and take a look at the code.

001.<#@ template language="C#" HostSpecific="True" #>
002.<#
003.MvcTextTemplateHost mvcHost = (MvcTextTemplateHost)(Host);
004.#>
005.using System;
006.using System.Collections.Generic;
007.using System.Linq;
008.using System.Web;
009.using System.Web.Mvc;
010. 
011.namespace <#= mvcHost.Namespace #>
012.{
013.    public class <#= mvcHost.ControllerName #> : Controller
014.    {
015.        //
016.        // GET: <#= (!String.IsNullOrEmpty(mvcHost.AreaName)) ? ("/" + mvcHost.AreaName) : String.Empty #>/<#= mvcHost.ControllerRootName #>/
017. 
018.        public ActionResult Index()
019.        {
020.            return View();
021.        }
022. 
023.<#
024.if(mvcHost.AddActionMethods) {
025.#>
026.        //
027.        // GET: <#= (!String.IsNullOrEmpty(mvcHost.AreaName)) ? ("/" + mvcHost.AreaName) : String.Empty #>/<#= mvcHost.ControllerRootName #>/Details/5
028. 
029.        public ActionResult Details(int id)
030.        {
031.            return View();
032.        }
033. 
034.        //
035.        // GET: <#= (!String.IsNullOrEmpty(mvcHost.AreaName)) ? ("/" + mvcHost.AreaName) : String.Empty #>/<#= mvcHost.ControllerRootName #>/Create
036. 
037.        public ActionResult Create()
038.        {
039.            return View();
040.        }
041. 
042.        //
043.        // POST: <#= (!String.IsNullOrEmpty(mvcHost.AreaName)) ? ("/" + mvcHost.AreaName) : String.Empty #>/<#= mvcHost.ControllerRootName #>/Create
044. 
045.        [HttpPost]
046.        public ActionResult Create(FormCollection collection)
047.        {
048.            try
049.            {
050.                // TODO: Add insert logic here
051. 
052.                return RedirectToAction("Index");
053.            }
054.            catch
055.            {
056.                return View();
057.            }
058.        }
059.         
060.        //
061.        // GET: <#= (!String.IsNullOrEmpty(mvcHost.AreaName)) ? ("/" + mvcHost.AreaName) : String.Empty #>/<#= mvcHost.ControllerRootName #>/Edit/5
062.  
063.        public ActionResult Edit(int id)
064.        {
065.            return View();
066.        }
067. 
068.        //
069.        // POST: <#= (!String.IsNullOrEmpty(mvcHost.AreaName)) ? ("/" + mvcHost.AreaName) : String.Empty #>/<#= mvcHost.ControllerRootName #>/Edit/5
070. 
071.        [HttpPost]
072.        public ActionResult Edit(int id, FormCollection collection)
073.        {
074.            try
075.            {
076.                // TODO: Add update logic here
077.  
078.                return RedirectToAction("Index");
079.            }
080.            catch
081.            {
082.                return View();
083.            }
084.        }
085. 
086.        //
087.        // GET: <#= (!String.IsNullOrEmpty(mvcHost.AreaName)) ? ("/" + mvcHost.AreaName) : String.Empty #>/<#= mvcHost.ControllerRootName #>/Delete/5
088.  
089.        public ActionResult Delete(int id)
090.        {
091.            return View();
092.        }
093. 
094.        //
095.        // POST: <#= (!String.IsNullOrEmpty(mvcHost.AreaName)) ? ("/" + mvcHost.AreaName) : String.Empty #>/<#= mvcHost.ControllerRootName #>/Delete/5
096. 
097.        [HttpPost]
098.        public ActionResult Delete(int id, FormCollection collection)
099.        {
100.            try
101.            {
102.                // TODO: Add delete logic here
103.  
104.                return RedirectToAction("Index");
105.            }
106.            catch
107.            {
108.                return View();
109.            }
110.        }
111.<#
112.}
113.#>
114.    }
115.}

You'll notice on line 13 that this is a controller class that inherits from Controller.  The mvcHost.ControllerName property contains the name of the controller that was entered in the AddController dialog.

Now by making some modifications to the template so it matches the working controller, I end up with this.

001.<#@ template language="C#" HostSpecific="True" #>
002.<#
003.MvcTextTemplateHost mvcHost = (MvcTextTemplateHost)(Host);
004.#>
005.using System;
006.using System.Collections.Generic;
007.using System.Linq;
008.using System.Web;
009.using System.Web.Mvc;
010. 
011.using Microsoft.Practices.Unity;
012. 
013.using GolfTracker.Services.Interfaces;
014.using GolfTracker.Mvc.Web.ViewData;
015.using GolfTracker.BusinessObjects;
016. 
017.namespace <#= mvcHost.Namespace #>
018.{
019.    public class <#= mvcHost.ControllerName #> : BaseController
020.    {
021. 
022.        [Dependency]
023.        public I<#= mvcHost.ControllerRootName #>Service service { get; set; }
024. 
025.        public ActionResult Index()
026.        {
027.            <#= mvcHost.ControllerRootName #>ViewData viewData = ViewDataFactory.CreateBaseViewData<<#= mvcHost.ControllerRootName #>ViewData>("<#= mvcHost.ControllerRootName #> List");
028.            viewData.<#= mvcHost.ControllerRootName #>List = service.GetAll();
029. 
030.            return View(viewData);
031.        }
032. 
033.<#
034.if(mvcHost.AddActionMethods) {
035.#>
036. 
037.        public ActionResult Details(Guid id)
038.        {
039.            <#= mvcHost.ControllerRootName #>ViewData viewData = ViewDataFactory.CreateBaseViewData<<#= mvcHost.ControllerRootName #>ViewData>("<#= mvcHost.ControllerRootName #> Details");
040.            viewData.<#= mvcHost.ControllerRootName #> = service.GetById(id);
041. 
042.            return View(viewData);
043.        }
044. 
045.        public ActionResult Create()
046.        {
047.            <#= mvcHost.ControllerRootName #>ViewData viewData = ViewDataFactory.CreateBaseViewData<<#= mvcHost.ControllerRootName #>ViewData>("Create <#= mvcHost.ControllerRootName #>");
048.            viewData.<#= mvcHost.ControllerRootName #> = new BusinessObjects.<#= mvcHost.ControllerRootName #>();
049. 
050.            return View(viewData);
051.        }
052. 
053.        [HttpPost]
054.        public ActionResult Create([Bind(Exclude="Id,rowversion")]<#= mvcHost.ControllerRootName #> model)
055.        {
056.            try
057.            {
058.                // Create new id
059.                model.ID = Guid.NewGuid();
060. 
061.                if (!ModelState.IsValid)
062.                    return RedirectToAction("Create");
063. 
064.                service.Insert(model);
065.                 
066.                return RedirectToAction("Index");
067.            }
068.            catch (Exception)
069.            {
070.                return RedirectToAction("Create");
071.            }
072.        }
073.          
074.        public ActionResult Edit(Guid id)
075.        {
076.            <#= mvcHost.ControllerRootName #>ViewData viewData = ViewDataFactory.CreateBaseViewData<<#= mvcHost.ControllerRootName #>ViewData>("Edit <#= mvcHost.ControllerRootName #>");
077.            viewData.<#= mvcHost.ControllerRootName #> = service.GetById(id);
078. 
079.            return View(viewData);
080.        }
081. 
082.        [HttpPost]
083.        public ActionResult Edit(<#= mvcHost.ControllerRootName #> model)
084.        {
085.            try
086.            {
087.                if (!ModelState.IsValid)
088.                    return RedirectToAction("Edit", new { id = model.ID });
089. 
090.                service.Update(model);
091. 
092.                return RedirectToAction("Index");
093.            }
094.            catch (Exception)
095.            {
096.                return RedirectToAction("Edit", new { id = model.ID });
097.            }
098.        }
099. 
100.        public ActionResult Delete(Guid id)
101.        {
102.            <#= mvcHost.ControllerRootName #>ViewData viewData = ViewDataFactory.CreateBaseViewData<<#= mvcHost.ControllerRootName #>ViewData>("Delete <#= mvcHost.ControllerRootName #>");
103.            viewData.<#= mvcHost.ControllerRootName #> = service.GetById(id);
104. 
105.            return View(viewData);
106.        }
107. 
108.        [HttpPost]
109.        public ActionResult Delete(<#= mvcHost.ControllerRootName #> model)
110.        {
111.            <#= mvcHost.ControllerRootName #> localModel = service.GetById(model.ID);
112.            try
113.            {
114.                service.Delete(localModel);
115.                return RedirectToAction("Index");
116.            }
117.            catch (Exception)
118.            {
119.                return RedirectToAction("Delete", new { id = model.ID });
120.            }
121.        }
122.<#
123.}
124.#>
125.    }
126.}

And when I create a new controller using your T4 template, this is the result.  As an example I'll create a new controller based on the Players model.

001.using System;
002.using System.Collections.Generic;
003.using System.Linq;
004.using System.Web;
005.using System.Web.Mvc;
006. 
007.using Microsoft.Practices.Unity;
008. 
009.using GolfTracker.Services.Interfaces;
010.using GolfTracker.Mvc.Web.ViewData;
011.using GolfTracker.BusinessObjects;
012. 
013.namespace GolfTracker.Mvc.Web.Controllers
014.{
015.    public class PlayersController : BaseController
016.    {
017. 
018.        [Dependency]
019.        public IPlayersService service { get; set; }
020. 
021.        public ActionResult Index()
022.        {
023.            PlayersViewData viewData = ViewDataFactory.CreateBaseViewData<PlayersViewData>("Players List");
024.            viewData.PlayersList = service.GetAll();
025. 
026.            return View(viewData);
027.        }
028. 
029. 
030.        public ActionResult Details(Guid id)
031.        {
032.            PlayersViewData viewData = ViewDataFactory.CreateBaseViewData<PlayersViewData>("Players Details");
033.            viewData.Players = service.GetById(id);
034. 
035.            return View(viewData);
036.        }
037. 
038.        public ActionResult Create()
039.        {
040.            PlayersViewData viewData = ViewDataFactory.CreateBaseViewData<PlayersViewData>("Create Players");
041.            viewData.Players = new BusinessObjects.Players();
042. 
043.            return View(viewData);
044.        }
045. 
046.        [HttpPost]
047.        public ActionResult Create([Bind(Exclude="Id,rowversion")]Players model)
048.        {
049.            try
050.            {
051.                // Create new id
052.                model.ID = Guid.NewGuid();
053. 
054.                if (!ModelState.IsValid)
055.                    return RedirectToAction("Create");
056. 
057.                service.Insert(model);
058.                 
059.                return RedirectToAction("Index");
060.            }
061.            catch (Exception)
062.            {
063.                return RedirectToAction("Create");
064.            }
065.        }
066.          
067.        public ActionResult Edit(Guid id)
068.        {
069.            PlayersViewData viewData = ViewDataFactory.CreateBaseViewData<PlayersViewData>("Edit Players");
070.            viewData.Players = service.GetById(id);
071. 
072.            return View(viewData);
073.        }
074. 
075.        [HttpPost]
076.        public ActionResult Edit(Players model)
077.        {
078.            try
079.            {
080.                if (!ModelState.IsValid)
081.                    return RedirectToAction("Edit", new { id = model.ID });
082. 
083.                service.Update(model);
084. 
085.                return RedirectToAction("Index");
086.            }
087.            catch (Exception)
088.            {
089.                return RedirectToAction("Edit", new { id = model.ID });
090.            }
091.        }
092. 
093.        public ActionResult Delete(Guid id)
094.        {
095.            PlayersViewData viewData = ViewDataFactory.CreateBaseViewData<PlayersViewData>("Delete Players");
096.            viewData.Players = service.GetById(id);
097. 
098.            return View(viewData);
099.        }
100. 
101.        [HttpPost]
102.        public ActionResult Delete(Players model)
103.        {
104.            Players localModel = service.GetById(model.ID);
105.            try
106.            {
107.                service.Delete(localModel);
108.                return RedirectToAction("Index");
109.            }
110.            catch (Exception)
111.            {
112.                return RedirectToAction("Delete", new { id = model.ID });
113.            }
114.        }
115.    }
116.}

As long as you have created a PlayersViewData class, this controller will compile and be ready to go.  As you can see, all the class names have been correctly concatenated with the proper entity name to make this class work.  And in the time it takes to create a new controller, you have one completely working.

Conclusion

Be aware that this will most likely just take care of the normal CRUD functionality, which is a big part of making your controllers work and much of the grunt work that is needed.  You will still have to manually create methods and code that will execute more custom processes.  Whenever you can use tools to reduce the amount of code you have to write, it just makes your work faster and cleaner.

So 90% of your controller code is done!  All I need to do now is write the custom code that I might need to enable further functionality for my application.

In the next episode I'll talk about modifying the T4 templates for the views so I can quickly created CRUD views for all my controllers.

Stay tuned and thanks for listening.

Comments

    No comments yet.

 

User Name:
(Required)
Email:
(Required)