SPA stands for Single Page Application. Here I will demonstrate a simple SPA with ASP.NET MVC, Web API and Entity Framework. I will show a trainer profile and its CRUD operation using AngularJS, ASP.NET MVC, Web api and Entity Framework.
Step 1: Create a ASP.NET MVC application with empty template
Open visual studio, Got to File->New->Project
Select Template -> Visual C# -> Web -> ASP.NET MVC 4 Web application and click OK
Select Empty Template and Razor as view engine
Step 2: Install required packages
Run the following command in Package Manager Console (Tools->Library Package Manager->Package Manager Console) to install required package. Make sure your internet connection is enabled.
PM> Install-Package jQuery
PM> Install-Package angularjs -Version 1.2.26
PM> Install-Package Newtonsoft.Json
PM> Install-Package MvcScaffolding
Step 3: Create Connection String
Create connection string and name DB name as SPADB
Step 4: Create model
Create Trainer model
public class Trainer { [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public long Id { get; set; } public string Name { get; set; } public string Email { get; set; } public string Venue { get; set; } }
Step 5: Create Repository
Create repository for Trainer model. Run the following command in Package Manager Console (Tools->Library Package Manager->Package Manager Console) to create repository. I used repository pattern for well-structured and more manageable.
PM> Scaffold Repository Trainer
After running the above command you will see SPAContext.cs and TrainerRepository.cs created in Model folder. For well manageability, I create a directory name Repository and put these two files in the Repository folder. So change the namespace as like SPA.Repository instead of SPA.Model. I also create a UnitOfWork for implement unit of work pattern.
The overall folder structure looks like following.
SPAContext.cs
public class SPAContext : DbContext { public SPAContext() : base("SPAContext") { } // You can add custom code to this file. Changes will not be overwritten. // // If you want Entity Framework to drop and regenerate your database // automatically whenever you change your model schema, add the following // code to the Application_Start method in your Global.asax file. // Note: this will destroy and re-create your database with every model change. // // System.Data.Entity.Database.SetInitializer(new System.Data.Entity.DropCreateDatabaseIfModelChanges()); public DbSet Trainers { get; set; } public DbSet Trainings { get; set; } }
TrainerRepository.cs
public class TrainerRepository : ITrainerRepository { SPAContext context = new SPAContext(); public TrainerRepository() : this(new SPAContext()) { } public TrainerRepository(SPAContext context) { this.context = context; } public IQueryableAll { get { return context.Trainers; } } public IQueryable AllIncluding(params Expression >[] includeProperties) { IQueryable query = context.Trainers; foreach (var includeProperty in includeProperties) { query = query.Include(includeProperty); } return query; } public Trainer Find(long id) { return context.Trainers.Find(id); } public void InsertOrUpdate(Trainer trainer) { if (trainer.Id == default(long)) { // New entity context.Trainers.Add(trainer); } else { // Existing entity context.Entry(trainer).State = System.Data.Entity.EntityState.Modified; } } public void Delete(long id) { var trainer = context.Trainers.Find(id); context.Trainers.Remove(trainer); } public void Save() { context.SaveChanges(); } public void Dispose() { context.Dispose(); } } public interface ITrainerRepository : IDisposable { IQueryable All { get; } IQueryable AllIncluding(params Expression >[] includeProperties); Trainer Find(long id); void InsertOrUpdate(Trainer trainer); void Delete(long id); void Save(); }
UnitOfWork.cs
public class UnitOfWork : IDisposable { private SPAContext context; public UnitOfWork() { context = new SPAContext(); } public UnitOfWork(SPAContext _context) { this.context = _context; } private TrainingRepository _trainingRepository; public TrainingRepository TrainingRepository { get { if (this._trainingRepository == null) { this._trainingRepository = new TrainingRepository(context); } return _trainingRepository; } } private TrainerRepository _trainerRepository; public TrainerRepository TrainerRepository { get { if (this._trainerRepository == null) { this._trainerRepository = new TrainerRepository(context); } return _trainerRepository; } } public void Dispose() { context.Dispose(); GC.SuppressFinalize(this); } }
Step 6: Add migration
Run the following command to add migration
PM> Enable-Migrations
PM> Add-Migration initialmigration
PM> Update-Database –Verbose
Step 7: Create API Controllers
Create Trainers api Controllers by clicking right button on Controller folder and scaffold as follows.
Step 8: Modify Controllers
Now modify the controllers as follows. Here I used unit of work pattern.
public class TrainersController : ApiController { private UnitOfWork unitOfWork = new UnitOfWork(); public IEnumerableGet() { List lstTrainer = new List (); lstTrainer = unitOfWork.TrainerRepository.All.ToList(); return lstTrainer; } //// GET api/trainers/5 public Trainer Get(int id) { Trainer objTrainer = unitOfWork.TrainerRepository.Find(id); return objTrainer; } public HttpResponseMessage Post(Trainer trainer) { if (ModelState.IsValid) { unitOfWork.TrainerRepository.InsertOrUpdate(trainer); unitOfWork.TrainerRepository.Save(); return new HttpResponseMessage(HttpStatusCode.OK); } return new HttpResponseMessage(HttpStatusCode.InternalServerError); } private IEnumerable GetErrorMessages() { return ModelState.Values.SelectMany(x => x.Errors.Select(e => e.ErrorMessage)); } // PUT api/trainers/5 public HttpResponseMessage Put(int Id, Trainer trainer) { if (ModelState.IsValid) { unitOfWork.TrainerRepository.InsertOrUpdate(trainer); unitOfWork.TrainerRepository.Save(); return new HttpResponseMessage(HttpStatusCode.OK); } else return new HttpResponseMessage(HttpStatusCode.InternalServerError); } // DELETE api/trainers/5 public HttpResponseMessage Delete(int id) { Trainer objTrainer = unitOfWork.TrainerRepository.Find(id); if (objTrainer == null) { return new HttpResponseMessage(HttpStatusCode.InternalServerError); } unitOfWork.TrainerRepository.Delete(id); unitOfWork.TrainerRepository.Save(); return new HttpResponseMessage(HttpStatusCode.OK); } }
Step 9: Create Layout and Home Controller
Create _Layout.cshtml in Views->Shared folder and create HomeController and create inext view of Home controller by right click on index action and add view. You will see index.cshtml is created in Views->Home
Home Controller
public class HomeController : Controller { // // GET: /Home/ public ActionResult Index() { return View(); } }
_Layout.cshtml
Training Registration @RenderBody()
Index.cshtml
@{ ViewBag.Title = "Home"; Layout = "~/Views/Shared/_Layout.cshtml"; }Home
Step 10: Create registrationModule
Create registrationModule.js in Scripts->Application. This is for angularjs routing.
var registrationModule = angular.module("registrationModule", ['ngRoute', 'ngResource']) .config(function ($routeProvider, $locationProvider) { $routeProvider.when('/Registration/Trainers', { templateUrl: '/templates/trainers/all.html', controller: 'listTrainersController' }); $routeProvider.when('/Registration/Trainers/:id', { templateUrl: '/templates/trainers/edit.html', controller: 'editTrainersController' }); $routeProvider.when('/Registration/Trainers/add', { templateUrl: '/templates/trainers/add.html', controller: 'addTrainersController' }); $routeProvider.when("/Registration/Trainers/delete/:id", { controller: "deleteTrainersController", templateUrl: "/templates/trainers/delete.html" }); $locationProvider.html5Mode(true); });
Step 11: Create trainerRepository
Create trainerRepository.js in Scripts->Application->Repository. This increase manageability for large application.
'use strict'; //Repository for trainer information registrationModule.factory('trainerRepository', function ($resource) { return { get: function () { return $resource('/api/Trainers').query(); }, getById: function (id) { return $resource('/api/Trainers/:Id', { Id: id }).get(); }, save: function (trainer) { return $resource('/api/Trainers').save(trainer); }, put: function (trainer) { return $resource('/api/Trainers', { Id: trainer.id }, { update: { method: 'PUT' } }).update(trainer); }, remove: function (id) { return $resource('/api/Trainers').remove({ Id: id }); } }; });
Step 12: Create trainerController
Create trainerController.js in Scripts->Application->Controllers
'use strict'; //Controller to get list of trainers informaion registrationModule.controller("listTrainersController", function ($scope, trainerRepository, $location) { $scope.trainers = trainerRepository.get(); }); //Controller to save trainer information registrationModule.controller("addTrainersController", function ($scope, trainerRepository, $location) { $scope.save = function (trainer) { trainer.Id = 0; $scope.errors = []; trainerRepository.save(trainer).$promise.then( function () { $location.url('Registration/Trainers'); }, function (response) { $scope.errors = response.data; }); }; }); //Controller to modify trainer information registrationModule.controller("editTrainersController", function ($scope,$routeParams, trainerRepository, $location) { $scope.trainer = trainerRepository.getById($routeParams.id); $scope.update = function (trainer) { $scope.errors = []; trainerRepository.put(trainer).$promise.then( function () { $location.url('Registration/Trainers'); }, function (response) { $scope.errors = response.data; }); }; }); //Controller to delete trainer information registrationModule.controller("deleteTrainersController", function ($scope, $routeParams, trainerRepository, $location) { trainerRepository.remove($routeParams.id).$promise.then( function () { $location.url('Registration/Trainers'); }, function (response) { $scope.errors = response.data; }); });
Step 13: Create templates
Create all.html, add.html, edit.html, delete.html in templateds->trainers folder.
All.html
Trainers Details
Add.html
Edit.html
Step 14: Add references to the Layout
Modify the _Layout.cshtml to add references
_Layout.cshtml
Training Registration @RenderBody()
Now run you application and add, delete, modify and get all trainer information. Thanks for your patient!
Excellent post
Very nice post. It saves my valuable time.
Nice post looking for further post here…………….
Really nice tutorial.
I have one problem. when i run the application it is working as expected.
for example when i click on Trainer Details link it will update the URL like http://localhost:8970/Registration/Trainers – this is working
But when i click on URL and press Enter then it is showing “The resource cannot be found.” i think it is related to URL mapping.
Can you please help me to resolve the issue.
Thanks,
Imdadhusen