We added a C# MVC Single Page Application (SPA) template in ASP.NET Fall 2012 Update BUILD Preview. John Papa had written a detailed blog about the preview version. With help of John Papa’s detailed feedback and sample code, we’ve rewritten and reorganized the JavaScript code to make it more structured in RC. We really appreciate the help from John Papa. He just wrote another blog about SPA template in RC version.
The major improvement for the MVC Single Page Template RC release includes the following:
1. Added VB MVC SPA template for .net framework 4.5 and 4.0.
2. Added WebAPI HelpPage support
3. Moved all the application specific JavaScript files to Scripts/app folder
4. Redo the datacontext, model and viewmodel files to make cleaner separation of data access, model and viewmodel layers
5. Created a namespace for the app called "todoApp"
6. Updated the knockout Nuget packages
Let’s discuss the important components for MVC SPA template using C#.
Introduction
The MVC SPA template is actually a sample application to demonstrate how to build a SPA application with MVC Web API. The application is able to create, read, update and delete (CRUD) todo-list with todo-items in each list for authenticated users.
At the server side, we use MVC Web API to create the scaffolding of our todo-list and todo-items models.
At the client side, we use Knockout JS framework and binding syntax to demo the Model-View-View Model pattern for SPA. We use JQuery $.ajax to send and receive JSON requests between client data access layer and server’s Web API layer. If you have not used KnockoutJS, you can use the links from the reference section to learn it.
To use the template, just choose “Single Page Application” template when you creates a new ASP.NET MVC 4 Project.
Layers
Server models
We have 2 basic entity framework models TodoItem and TodoList.
TodoList entity contains a list of TodoItems.
TodoItem entity has a foreign key TodoListId and a virtual TodoList to represent the relationship with TodoList entity.
The default JSON/XML serialization of these two models, however, will run into circular reference problems. Here are a few solutions to solve them. If the project only uses JSON, it can be solved quite simply by adding a “[JsonObject(IsReference = true)]” attribute to TodoList class.
The default XML serialization could also fail when you have the EF proxy and lazy loading enabled. The issue arises when you try to serialize a proxy of TodoList which really isn’t a TodoList but a dynamically generated type. Thus the following exception is thrown:
Type 'System.Data.Entity.DynamicProxies.TodoList_4ECA216B8773C8B6552AD787C3BA8087E9C365199E3C2F3C1E28B89CD6556441' with data contract name TodoList_4ECA216B8773C8B6552AD787C3BA8087E9C365199E3C2F3C1E28B89CD6556441:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer
In our template, we demonstrated how to use Data Transfer Object (DTO) to solve both problems so that it works for XML and JSON serialization. DTO is a preferred design pattern for solutions with complex data models. You can see the DTO classes from TodoItemDto.cs and TodoListDto.cs files.
Server Web API DBContext
The TodoItemContext.cs file provides the Database Context that Entity framework will use. This file would normally be generated and modified via data model Web API Scaffolding.
Server Web API Controllers
Controllers\TodoController.cs and TodoListController.cs files contain the Web API controller code. When we built the SPA application for template, we generated the files through Web API scaffolding, and then modified them to work with DTOs and authorizations. Controller class attribute “[Authorize]” ensures only authorized user can access these Web API controller calls. Checking against User.Identity.Name inside most functions is to ensure that the database records can only be accessed by their corresponding owners.
The TodoListController.GetTodoLists function has an include statement in its LINQ call. It’s because our default connection string does not have MultipleActiveResultSets=”true” set (MARS). You can modify the SQL connection string locally if you want MARS behavior. For Windows Azure deployment, you need to manually modify its SQL Azure connection string to include MARS if you choose to do so.
Server View and Client View with Knockout JS
In Views\Home\Index.cshtml, we use the knockout data-bind attributes as the view. You can see all the knockout data-bind attributes are colorized and have limited intelliSense available for RC. In order to get the knockout binding handlers and user defined handlers’ intelliSense, we need to reference the knockout and app/todo*.js files in Scripts/_references.js file.
Client JavaScript Data Access Layer
Scripts/app/todo.datacontext.js file defines the todoApp namespace and the data access layer to communicate with the server Web API controllers. One thing to note is the usage of $.ajax with “cache: false” inside its options parameter, because in certain browsers, JSON requests can be cached and we will not be able to get updated Web API results. One thing we might improve in the future for this file is to remove the knockout relationship in this file so that any other SPA framework can work with this file without changing it.
Client JavaScript Models
Scripts/app/todo.model.js file defines the knockout data models. Besides the general knockout observable defines, it also shows how to subscribe a function to an observable so that function will get called whenever the observed property changes.
Client JavaScript ViewModels
Scripts/app/todo.viewmodel.js defines the view model for the page. It defines the page level view model and initiates the knockout bindings for the page.
Knockout Bindings JavaScript File
Scripts/app/todo.bindings.js defines customized knockout binding handlers which demonstrate how to extend knockout JavasSript framework. We also extended the knockout binding handlers to work with JQuery Validation plugin.
AjaxLogin.js file
Scripts/app/ajaxlogin.js shows how we use AJAX to do form based login with JSON. We also have the code to handle the login/register link actions.
Summary
In MVC SPA template, we showed a SPA design pattern with KnockoutJS framework, to work with server end MVC Web API. Our purpose is to provide a simple starting point for people to study knockoutJS SPA framework with MVC Web API, instead of trying to fully cover all SPA aspects, which also includes routing and much more.
Knockout can also be easily replaced with other SPA JavaScript frameworks that you feel comfortable with. Don’t be shy to study the difference of all the SPA frameworks out there.
References
John Papa’s blog about our ASP.NET and Web Tools 2012.2 RC SPA template
John Papa’s blog about our ASP.NET Fall Update 2012 Build Preview SPA template