1 more day to go and I'm looking forward to a full day on MVC! I have become a big fan of MVC over the last year, previously being a web forms developer for the past 9 or so years. The agenda looks great (this is not some intro course) so let's get to it!
Mastering ASP.NET MVC4 in Just One Day
Tiberiu Covaci, Microsoft MVP and Senior Trainer & Mentor
I'm looking at the agenda for today and it looks like we will be covering soup to nuts on MVC. Obviously there are a lot of web forms developers in attendance that are seeking out knowledge on MVC, so there has to be a general introduction to 'Model-View-Controller' As always the typical diagram displaying the general interaction between the parts is displayed below:
If you are new to MVC there are some goals, even say 'advantages', to this framework. Tibi mentioned testability, tight control over markup leverage the benefits of ASP.NET 4, and conventions and guidance. I actually spoke in depth to some of these in a post I did a couple of years ago and you can look at it here: ASP.NET Web Forms vs. ASP.NET MVC
One of the other main features of MVC is its extensibility. ASP.NET MVC is actually open source, so modifying it to your needs presents an endless about of customizations For example, you don't have to use just the .aspx or Razor View engines. Others are available like Spark if you want to use it. Personally I'm a big fan of the Razor view engine out of the box and it fits my needs. However it's nice to have that flexibility if needed. Now with NuGet, you can get a slew of different packages to help with development. In fact if you strictly work with blinders on from only using out of the box MVC functionality, you are probably missing out on something that will make developing your application easier, How do you find out about the packages available Blogs, conferences, magazine article, word of mouth from experience, etc.
It's always tough when explaining to those new to MVC which piece to explain 1st (Model-View-Controller). Starting with the Model is a good idea as you can really think of these as your old business logic layer classes. On this note a few of the architectural patterns were brought up that can used in MVC: Repository, DDD (big fan), and service pattern. If you are not familiar with DDD, in a nutshell the focus of the architecture is on the business domain, and after all this should be the focus of the application. Someday I would like to see a MVC implementation using DDD, but today we will be using the Repository pattern which I am also a big proponent.
The example used today had a single model class named 'Course'. This is where the getters/ setters are for the object and any method calls not directly related to persistence The repository pattern is responsible for the interaction with the persistence layer (i.e. Entity Framework) and wraps the basic CRUD methods. To keep the focus on MVC, there was not any true implementation in the repository to fetch data, and the data was loaded from static data in a mock layer named 'TibiPublicSite.MockRepository'. The CourseRepository implemented ICourseRepository, and returned hardcoded/mocked up data. Works perfectly because this is not a session on EF or other ORM.
One thing I should mention that was being done in the solution referenced throughout the day was the layering of the application. You will notice by default that an MVC project will have all of the pieces in a single solution. This is ok and I recommend for beginners to just stick with this until they get the hang of the method calls and flow. However, MVC can be considered a and architecture for a UI only and this is why separating out the additional layers is a good idea. Here are the layers from today's application:
- TibiPublicSite (MVC)
- TibiPublicSite.MockRepository
- TibiPublicSite.Models (contains repository interfaces and model classes)
The next key piece of the MVC architecture are the 'Controllers'. These are the server side classes, and each MVC request maps to a Action method in a Controller class Remember, MVC uses routing, and the URL route is dictating an action on the controller to be executed. The classes here implement 'IController'.
An Action is a method that returns a ActionResult. ActionResult is the base class, but there are other result types as well like ViewResult, RedirectResult, and JsonResult to name a few. When you add a controller, you can actual scaffold it based on an existing class (make sure to build your project 1st so they are available for selection This will create many of the action methods on the controller already needed based on the model class selected.
Using Dependency Injection on the Controller is a great was to loosely couple the persistence mechanisms from the controller itself. When the controller knows too much about the actual persistence details, you create tightly coupled code that is less reusable and more difficult to maintain in the future. Having the controller have intimate knowledge and making direct calls to the database is not a good idea. The repository is injected into the interface on the controller and is accessible by the methods on the class. It's better to call _repository.Save(myObject), rather than having to know how to directly work with the Entity Framework context, ADO.NET code, or other persistence mechanisms. You might be thinking that you could still push persistence details down to the model, but by injecting the repository into the Controller, you set yourself up much better for unit testing.
However to take this a step further in reality there is not a 1 to 1 relationship between the View and the Model. Therefore we will be using customized classes that contains a combination of data that may span > 1 model class. These classes are named 'ViewModels'.
HTTP verbs are decorated on controller actions in the form of attributes. The HttpGet is not required as it is the default. You can still add it for consistency and to be explicit. If you try and call an action with the incorrect verb it will obviously not get called.
It is important in controller actions to check if (ModelState.IsValid) to ensure model binding worked correctly.
Routing is a part of ASP.NET System.Web.Routing. /Bikes/MountainBike is much better that Products.aspx?Item=MountainBike Routing is defined in Application_Start() and contains one default route {controller}/{action}/{id}. The ordering it is listed in the class is important. The routing process is to trickle down top to bottom to find the 1st match. If you have a match that was unintended, you might get the wrong controller action.
You know sandwiched here in the middle I have to say how exciting some of these technologies are to work with. Take a look at the following line of code:
var model = _repository.GetByCriteria( c=> c.Title.Contains(searchTerm));
MVC, Lambdas, Dependency Injection, entity framework, dynamic typing. One line of code that does so much would have taken many times the effort if not using these technologies. This is why it's so important to stay current, because otherwise you may be working 10x the effort when there is a better way.
Route debugging can be aided by a NuGet package created by Phil Haack named 'RouteDebugger'. This is a fantastic tool that when navigating to a URL, you will get the bottom portion of the browser breaking down the route and which patterns it matched and any constraints This will help for what I discussed previously about trying to determine which route from Application_Start that the URL matched. Just remember to disable this in configuration before deployment or when not needed:
You know he started talking about web.config transformations and this is something I'm embarrassed to say I have not used. The result, over complication in code or going back and toggling values between test and production. Essentially is does a XSLT transformation on the web.config file for both 'Debug' and 'Release' scenarios. Need to start using this.
Views are another major component of MVC. They are as close to a page as it could get. Strongly typed views inherit from ViewPage
The Razor View Engine syntax is slick. I actually did not start using MVC until v3 so I never got into the .aspx engine syntax. In fact if you were driven away from MVC because it looked like classic ASP, give it another look and try out the Razor syntax. Razor syntax uses HTML helpers which can be accessed in the view via Intellisense by starting with the '@' symbol. Remember when adding a new Razor view, the difference between a 'Partial' view or not is the inclusion of the _Layout view. Partial views will not include it.
Tibi forgot to add the @RenderBody() helper to _Layout.cshtml (think of the _Layout view like a Master Page). This helper is required and is analogous to the ContentPlaceHolder server control in a MasterPage in web forms. The @RenderBody method indicates where view templates that are based on this master layout file should “fill in” the body content.
Make sure to declare the model directive at the top of the view like @model IEnumerable
The razor syntax HTML helpers flows very nicely in with existing HTML. With razor syntax JS is added in a section called @section scripts{} and CSS is in a section called @section styles{} You can also use the @RenderSection("scripts", false) helper in the _Layout view to dictate a section in other views. If you use JS or CSS on most views, this will keep it consistent in placing.
For those new to MVC and the Razor engine the HtmlActionLink will be your friend for making links on a page that route to controller actions. Remember there is no physical page on the server (like web forms). We have to route to an action on a controller to execute our code on the server. An example of this is the following: @Html.ActionLink("Start", "Start", "Courses"). This makes a link named 'Start', that will route to the 'Start' method on the 'Courses' controller. Remember MVC is based on convention and not configuration like web forms so you do not have to write 'CoursesController' because 'Controller' is redundant and known.
If you want to pass parameters you would pass them as object routeValues: @HtmlActionLink("Details", "Details", new{id = item.id}, new{@class = "abc"}). Notice using '@' in front of class because 'class' is a reserved keyword.
The @using (Html.BeginForm()) wraps where the form starts and begins, and uses IDisposable to be disposed when not used anymore.
Model Binding is probably my favorite thing in MVC. Remember the days of having to explicitly pull all form values server side to send off for manipulation (i.e. SaveValues() or something)? Not anymore. Model Binding will automatically send form values back to the controller and populate the object in the controller method parameter list. This saves on a lot of redundant code. I guess I get more excited about this because I did web forms for so many years and would sometimes have large methods to scrape values off controls into a business object.
To add to how easy this all wires up, you can scaffold the View off a Model or ViewModel class and have all the markup created for you! Again, this was a lot of manual work in web forms (unless using a FormView or something but the server side operations had a heavy footprint and got messy quick). When scaffolding, you can select the class and type of View to create. The screen below shows how I scaffold a 'Create' View for 'Courses' model class:
Want even more streamlined? Use @Html.EditorForModel() on the view for the bound model. What you get from a single line is the entire form of fields to edit! One line, are you kidding me? Again, us old web forms folks appreciate this a a lot. By the way if you are wondering how you might exclude or control which fields show up on the form, because maybe you don't want SSN shown or at least disabled. You can control this by using data annotations on the model properties that the fields map to. Have a look at the System.ComponentModel.DataAnnotations namespace for the fields available.
The annotations also work hand in hand with the jQuery validation scripts. Remember having to explicitly add all the RequiredFieldValidators or JS clientside? Not anymore, because ASP.NET will use these annotations to automatically do fields validation based on the presence of the jQuery validation scripts. In fact, these scripts will already be in your project upon creation, so you have to do very little to get it all working.
Annotations do (even with me) seem a bit like decorating values for UI purposes a few layers down which might be viewed as improper. An alternative is to create a MetaData class that abstracts away the annotations. So I might create a class called 'public class CourseMetadata' and decorate the model class with [MetadataType(typeof(CourseMetadata))]. Just remember the annotations are good for EF as well if doing a 'Code First' approach as it will define the constraints on the fields in the database. I do admit abstracting away the annotations into a MetaData class is pretty nice and probably something I will implement in future applications. The only downside might be developers coming behind not initially seeing the annotations where they expect them (on the Model class). This is why commenting never hurts. Just add a little note in the XML block on the class.
If you happen to being doing mobile apps, you could add separate versions of the View using the nomenclature 'index.mobile.cshtml'. This is a nice way to differentiate views. I would not be surprised in the next 2-5 years that a feature is available to streamline this duplication of Views between desktop and mobile Views. Today you still have to create separate views. Remember (from the other day), the user-agent is pulled when accessing the site, and ASP.NET knows how to serve up which version of the views. Check out "Framework64\v4..0.30319\Config\Browsers\" to get a list of the user-agent browser files. You can open these files in VS.NET if you want to inspect them. After the proper user agent is matched, then the view name by convention (index.mobile.cshtml) is rendered.
"How many want to hear about Web API?" The entire room raised their hands! How appropriate to finish the day and conference with a technology at the top of my list in interest in learning. It allows the creation of REST based services.
Now here is something to wrap your head around. Just learning MVC? Well there is a camp of folks that are writing application by making all calls to the controller via Web API. One of the goals here is to push more functionality to the client and prevent expensive server calls. Libraries like Knockout.js have to be leveraged to help in this. By doing this you make lean responsive applications. It's a balance between what goes on the client and what's on the server. One thing at a time for now...
Tibi added a folder to the existing MVC app named 'api' under the existing 'Controllers' folder. The 'api' will match the convention for the Web API URIs. This folder will contain the controller actions for 'api' calls. Notice in the routing (Application_Start) that no 'Action' is defined. This is because the Http verb infers this for us in API calls.
Using Fiddler is the easiest way to make API calls. Just click on the 'Composer' tab and select the proper Http verb along with the URL to call. You can also modify the request headers or body from here as well. This is a nice place to define the user-agent if you are testing that functionality. To build up the body, create JSON like {"Title": "My New Course", "Duration": "1"}. Make sure in the header to include "Content-type: application/json" (without quotes).
He finished off with a quick demo on a Dependency Resolver named Unity (Unity.MVC or Unity.WebAPI; there are different versions for different technologies). Unity is a lightweight, extensible dependency injection container that supports interception, constructor injection, property injection, and method call injection. You can get Unity from NuGet and add it as an installed package to your application. This way you do not have to manually instantiate the model type on the repository for each class. In Application_Start() you make a call to Bootstrapper.Initialize() to register the containers that map the Types to their concrete equivalent.
container.RegisterType
Whew, that was a great day with a lot of information on MVC. Nice job Tibi!
Wrap Up Day 5 - That's a wrap!
Well it's the last day of the conference, and the 1st day the sun has been out since prior to conference registration! Sorry to the people that came here from the north and expected to see the 'sunshine state' along with 75 degree weather.
It's bittersweet at the end of the conference because it's sad it's over but it will be nice to go home. The comradery among attendees and networking that occurs is an awesome byproduct of the conference. My brain is absolutely jam packed with good information. I applaud all of the presenters and conference organizers and rank this as one of the best Visual Studio LIVE! conferences I've attended. If you have never attended a conference like this, I highly recommend breaking out of your cultural "bubble" at work, home, etc. and attend to see where the community is headed. These conferences doing a job second to none to validating and presenting a sort of "what's good and what's maybe not so good" based on community feedback (presenters don't pitch anything and just state the facts).
I am already looking forward to next year's conference, but in the meantime I feel I have the information needed to make great decisions on leading technology for the upcoming 2013 year. Hope everyone has a great trip home and see you next year! #Live360