A simple SPA with AngularJs, ASP.NET MVC, Web API and EF

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.

st-final

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 IQueryable All
        {
            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.

TrainersController-2

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 IEnumerable Get()
        {
            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

Name Email Venue Action
{{trainer.name}} {{trainer.email}} {{trainer.venue}} Delete |Edit

Add.html


Trainer Name
Email
Venue
Cancel

Edit.html

Name
Email
Venue

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!

Please share, if it is helpfulShare on FacebookShare on LinkedInTweet about this on TwitterShare on Google+Email this to someonePrint this page

4 thoughts on “A simple SPA with AngularJs, ASP.NET MVC, Web API and EF

  1. Imdadhusen says:

    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

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>