4.27.2017

AngularJS CRUD CheckList

Introduction

I have created a detailed walkthru for the step-by-step creation of an AngularJS CRUD application using WEB API, MVC 5, Entity Framework at

But I find a simple checklist can be of great aid to remember all the little details to be done. Hence this is that list.

Task ID Task/Step
1Design database
2Create seed scripts
3Create empty Web Application Project, add MVC and WEB API References
4Add UnitTest and Datalayer projects to the solution
5Add NuGet packages to projects.
6DataLayer Project, add the ADO.NET Entity Data Model
7DataLayer Project, create IRepository/Repository pattern.
8UnitTest Project, add reference to DataLayer Project - copy local the DLL
9UnitTest Project, add MoQ and NUnit frameworks
10UnitTest Project, create failing unit test, then create passing test
11Web Project, add reference to DataLayer Project - copy local the DLL
12Web Project, Add empty Web API 2 Controller
13Web Project, add reference to IRepository to new Web API Controller, implement methods in interface.
14Web Project, add new empty MVC 5 Controller
15Web Project, add new _LayoutPage.cshtml to Shared folder
16Web Project, add new class named BundleConfig.cs to App_Start folder
17Web Project, You may need to install the Microsoft.AspNet.Web.Optimization via NuGet - without it the BundleCollection will not instantiate.
18Web Project, modify Global.asax.cs add BundleConfig.RegisterBundles(BundleTable.Bundles);
19Web Project, using the new HomeController add a new View referencing the _LayoutPage.cshtml in Shared.
20Web Project, add the following to the new Index.cshtml to reference the bundles @Styles.Render("~/Content/VendorCss"), and @Scripts.Render("~/Scripts/VendorJavaScript")
21Web Project, add the ng-app and ng-view to the Index.cshtml
22Web Project, create the AngularJS folder structure see John Papa
23Web Project, create app.js
24Web Project, create service for WEB API methods.
25Web Project, create controller
26Web Project, create view
27Web Project, update Index.cshtml to reference @Scripts.Render("~/bundles/Application")
28Web Project, add Json Formatter settings to Global.asax.cs
29Update All App.Config and Web.Config files, make sure the Datalayer is copy local for non Datalayer projects.
30TURN IGNITION

4.15.2017

AngularJS CRUD, Entity FrameWork & Web API Step-By-Step Part Two

Introduction

This is Part Two of my Angular Step-By-Step tutorial.

Add BundleConfig.cs

  1. Select the App_Start folder. Right-Click and choose the Add from the context-menu.
  2. Add a new Class name it BundleConfig.cs.
  3. This class will use the following namespace using System.Web.Optimization;.
  4. Add the following static method public static void RegisterBundles(BundleCollection bundles)
  5. You may need to install the Microsoft.AspNet.Web.Optimization via NuGet - without it the BundleCollection will not instantiate.
  6. Now to configure this class to load the bundles. We will use the .IncludeDirectory method. Type the following code in the RegisterBundles method:
           bundles.Add(new StyleBundle("~/Content/VendorCss")
                .Include("~/Content/bootstrap.css")
                .Include("~/Content/bootstrap-theme.css")
                .Include("~/Content/font-awesome.css")
                .Include("~/Content/toastr.css"));

            bundles.Add(new ScriptBundle("~/Scripts/VendorJavaScript")
                .Include("~/Scripts/jquery-3.1.1.js")
                .Include("~/Scripts/bootstrap.min.js")
                .Include("~/Scripts/angular.js")
                .Include("~/Scripts/angular-route.js")
                .Include("~/Scripts/toastr.js")
                .Include("~/Scripts/lodash.js"));

Modify the Global.asax.cs File

  1. In the Solution Explorer scroll to the top, select Solution WorldPopulation.
  2. Select the Project WorldPopulation
  3. Expand the file named Global.asax.
  4. Open the file Global.asax.cs
  5. Add the following code to the Application_Start method
BundleConfig.RegisterBundles(BundleTable.Bundles);

NOTE You will need to add a reference to using System.Web.Optimization;.

the Global.asax.cs should now look something like the following:

    public class Global : HttpApplication
    {
        void Application_Start(object sender, EventArgs e)
        {
            // Code that runs on application startup
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }

Adding a RAZOR Index.cshtml page

Open the HomeController that was added in Step #7.

  1. The View() will be RED because there is no Index View (remember Model-View-Controller). Right-click on the word Index and select Add View from the popup.
  2. The next dialog will have several options, pick the above Layout Page you just added then click Add.
  3. Press Add.

Now this will insert a basic HTML page, and we need to modify it so the CSS and JavaScript bundles get loaded, and the Single Page Application will be setup.

Modify the Index.cshtml file to look like the following

 @{
    ViewBag.Title = "Index";
    Layout = "~/Views/_LayoutPage.cshtml";
}

<!DOCTYPE html>

@using System.Web.Optimization
<html ng-app="worldPopApp">
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>

    @Styles.Render("~/Content/VendorCss")

    @Scripts.Render("~/Scripts/VendorJavaScript")

    @Scripts.Render("~/bundles/Application")
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">

        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a asp-area="" asp-controller="Home" asp-action="Index" class="navbar-brand">World Population</a>
        </div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                @*<li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
                    <li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>
                    <li><a asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li>*@
            </ul>
        </div>

    </div>
    <hr /><hr />
    <div class="main container-fluid" ng-view>

    </div>
</body>
</html>

NOTE There is a bundle reference in this code to @Scripts.Render("~/bundles/Application"). This does not exist in the BundleConfig. We will set this up, and it will not cause the code to crash. It will throw an exception to the browser console.

Angular Directive ng-app

In the Index.cshtml there is a line of code that tells this web application it is an AngularJS application

<html ng-app="worldPopApp">

Use this directive to auto-bootstrap an AngularJS application. The ngApp directive designates the root element of the application and is typically placed near the root element of the page - e.g. on the or tags. There are a few things to keep in mind when using ngApp:

  • only one AngularJS application can be auto-bootstrapped per HTML document. The first ngApp found in the document will be used to define the root element to auto-bootstrap as an application. To run multiple applications in an HTML document you must manually bootstrap them using angular.bootstrap instead.
  • AngularJS applications cannot be nested within each other.
  • Do not use a directive that uses transclusion on the same element as ngApp. This includes directives such as ngIf, ngInclude and ngView. Doing this misplaces the app $rootElement and the app's injector, causing animations to stop working and making the injector inaccessible from outside the app.
You can specify an AngularJS module to be used as the root module for the application. This module will be loaded into the $injector when the application is bootstrapped. It should contain the application code needed or have dependencies on other modules that will contain the code. See angular.module for more information.

Angular Directive ng-view

In the Index.cshtml there is a line of code that injects web pages into Index.cshtml between the div.

Understanding SPA and ng-view can be found here: Single Page Apps with AngularJS Routing and Templating


    <div class="main container-fluid" ng-view>

    </div>

The basic startup view is setup, now we need to setup the AngularJS folder structure, app and routing.

AngularJS Folder Structure

As noted in AngularJS CRUD, Entity FrameWork & Web API Step-By-Step Part One the folder structure we will use is what John Papa recommended years ago, as defined here John Papa project structure.

Steps to Creating the Folder Structure

  1. Select the WorldPopulation Project.
  2. Right-Click the WorldPopulation Project, select New Folder from the context menu.
  3. Name the folder app (Case is critical)
  4. Select the newly created folder app, Right-click it and select New Folder from the context menu.
  5. Name the new folder controllers.
  6. Select the newly created folder app, Right-click it and select New Folder from the context menu.
  7. Name the new folder services.
  8. Select the newly created folder app, Right-click it and select New Folder from the context menu.
  9. Name the new folder views.

Now the basic folder structure for the AngularJS components is completed.

AngularJS Setup app.js

  1. Select the folder app. Right-click it and select Add from the context menu. Select JavaScript File.
  2. Type app in the popup dialog. Press OK

You are presented with an empty file. Paste in the code from below.


var app = angular.module('worldPopApp',
    [
        'ngRoute',
        'toastr'
    ]);

app.config([
    '$routeProvider', function ($routeProvider) {
        $routeProvider.when('/',
            {
                templateUrl: 'app/views/wpList.html',
                controller: 'wpListCtrl'
            }).otherwise({
                redirectTo: '/'
            });
    }
]);

NOTE I have identified an issue with Twitter Bootstrap v3.3.7 as of March 2017, that seems to cause an issue with the Angular UI-Grid - a really nice grid, not being used in this tutorial but one I use extensively over the Kendo-Grid. Just keep in mind, I had to down step my NuGet package for Bootstrap to use the UI-Grid in my personal projects, but I suspect this will be fixed soon.

Injecting the 'toastr' library may be an issue, I am debugging, so if you get any console errors simply comment it out using // and comment out the comma after the injection of the 'ngRoute'.

Now, lets add a VIEW, CONTROLLER and SERVICE. If you don't know what these are I strongly recommend you take any AngularJS 1.x tutorial. In the folder app/views insert the following code into a file named wpList.html (CASE IS IMPORTANT)


<style>
    .tablerow:hover, .tablecell:hover { background-color: #f4a460; }

    table { overflow: hidden; }

    .tablecell { position: relative; }

    .tablecell:hover::before {
        content: "";
        position: absolute;
        left: 0;
        top: -5000px;
        height: 10000px;
        width: 100%;
        z-index: -1;
        /* keep it below table content */
        background-color: #f4a460;
    }

    .tableheader { text-align: center; }
</style>
<div ng-controller="wpListCtrl as vm" ng-init="vm.init()">
    <div class="table-responsive">
        <table class="table table-striped table-bordered">
            <tr>
                <th style="width: 2%;" class="tableheader">Id</th>
                <th style="width: 90%;" class="tableheader">Country</th>
                <th style="width: 4%;"></th>
                <th style="width: 4%;"></th>
            </tr>
            <!--<pre>{{vm.worldpopulation.data | json}}</pre>-->
            <tr class="tablerow" ng-repeat="item in vm.worldpopulation.data">
                <td class="label-success">{{ item.COUNTRYID }}</td>
                <td>
                    <a class="btn btn-link" ng-click="vm.addCountry(item)" style="text-decoration: underline !important; padding: 0 !important; margin: 0 !important;"
                       href="">{{ item.COUNTRY_NAME }}</a>
                </td>
                <td>
                    <a href="#/edit/{{item.COUNTRYID}}" class="glyphicon glyphicon-edit"></a>
                </td>
                <td>
                    <a href="#/delete/{{item.COUNTRYID}}" class="glyphicon glyphicon-trash"></a>
                </td>
            </tr>
        </table>
    </div>
</div>

In the folder app/services insert the following code into a file named wpService.js (CASE IS IMPORTANT)


app.factory("wpService", ["$http", "$q",
    function ($http, $q) {
        var getWorldPopulationData = function () {
            var deferred = $q.defer();
            $http({
                method: 'GET',
                url: '/api/worldpopulation'
            }).then(function (dataResponse) {
                deferred.resolve(dataResponse);
                }, function (dataResponse) {
                    alert("error" + dataResponse);
                deferred.reject();
            });
            return deferred.promise;
        };

        var deleteWorldPopulation = function (data) {
            var deferred = $q.defer();
            $http.delete('/api/worldpopulation/' + data.COUNTRYId).then(function () {
                deferred.resolve(true);
            }, function () {
                alert("error");
                deferred.reject();
            });
            return deferred.promise;
        }

        var saveWorldPopulation = function (data) {
            var deferred = $q.defer();
            $http.post('/api/worldpopulation/', data).then(function () {
                deferred.resolve(true);
            }, function () {
                alert("error");
                deferred.reject();
            });
            return deferred.promise;
        }
        return {
            GetWorldPopulationData: getWorldPopulationData,
            DeleteWorldPopulation: deleteWorldPopulation,
            SaveWorldPopulation: saveWorldPopulation
        }
    }]);

In the folder app/controllers insert the following code into a file named wpListCtrl.js (CASE IS IMPORTANT)


(function () {
    'use strict';
    var controllerId = 'wpListCtrl';
    app.controller(controllerId, ['$scope', '$http', '$location', 'wpService', wpListCtrl]);

    function wpListCtrl($scope, $http, $location, wpNotesService) {
        var vm = this;
        vm.data = wpNotesService;
        vm.sort = 0;
        vm.currentSort = '';
        vm.moduleTitle = 'World Population Countries List';
        vm.worldpopulation = [];
        vm.isActive = false;
        vm.ViewModel = {
            StatusCode: 'A'
        }


        vm.init = function () {
            vm.loadData();

        };

        vm.loadData = function () {
            vm.data.GetWorldPopulationData().then(function (data) {
                vm.worldpopulation = data;
            });
        }

        vm.GetStatus = function (data) {
            if (data.StatusCode === 'A')
                vm.isActive = true;
            vm.isActive = false;
        };
    }
}
)();

Modify the Global.asax.cs File

We need to modify the Global.asax.cs again because our data is coming from the database in EntityFramework and the properties are not serializing the data into a format that can be used easily in the client. so below the Application_Start add the following:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
           GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

Before this modification, when running the application you may see an error such as: The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'." There are several means to address this like

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

But this solution is wrong. This is JSON and XML Serialization in ASP.NET Web API

4.12.2017

Software Design Patterns

I remember when on an interview they asked me what patterns we used. The development team I was on had never used them - I said that and never heard from that recruiting lead again... No, big loss, it made me dig into the patterns, learn them and start applying them. While C# was my native coding language, I first applied them on a Java project. Well now we use them all over.

AngularJS CRUD, Entity FrameWork & Web API Step-By-Step Part One

Introduction

I am a full-stack web programmer for the State Department. We work with AngularJS 1.2 mixed with WEB API, WCF and SQL Server - our own hybrid architecture.

It is how I learned this tech. But I wanted to create other Angular based projects, some to help my friends with their hobbies (feral Cats), others to simply solidify my AngularJS understanding - just before I take the leap into AngularJS 2.0…

So I created lots of CRUD apps, and I like the John Papa project structure - so this post is about building an AngularJS CRUD project using the MVC and WEBAPI template, and Entity Framework

Project Requirements

The project will be based on this table at the website wikipedia. This is a table that lists countries by population and captures the change by year in population.

Scope of Step #1

This step simply gets the database up and running.

Data Model

In this project example, I am going to use the Entity Framework Database First - using Microsoft SQL Server.

Scripts

The database scripts are as follows:

CREATE TABLE COUNTRY(
    COUNTRYID int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
    COUNTRY varchar(100) NULL,
    CONTINENTIAL_REGIONID int NULL,
    STATISTICAL_REGIONID int NULL
) 
GO

CREATE TABLE UN_CONTINENTAL_REGION(
    CONTINENTIAL_REGIONID int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED ,
    CONTINENTIAL_REGION varchar(250) NULL
) 

GO

CREATE TABLE UN_STATISTICAL_REGION(
    STATISTICAL_REGIONID int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
    STATISTICAL_REGION varchar(250) NULL
    )

CREATE TABLE dbo.[POPULATION](
    POPULATIONID int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED ,
    COUNTRYID int NULL,
    YEARID int NULL,
    POPULATION int NULL
    )
GO

CREATE TABLE YEAR_DOMAIN(
    YEARID int NOT NULL PRIMARY KEY CLUSTERED
    )
GO

ALTER TABLE COUNTRY  WITH CHECK ADD  CONSTRAINT FK_COUNTRY_UN_CONTINENTAL_REGION 
FOREIGN KEY(CONTINENTIAL_REGIONID) REFERENCES 

UN_CONTINENTAL_REGION (CONTINENTIAL_REGIONID)
GO

ALTER TABLE COUNTRY CHECK CONSTRAINT FK_COUNTRY_UN_CONTINENTAL_REGION
GO

ALTER TABLE COUNTRY  WITH CHECK ADD  CONSTRAINT FK_COUNTRY_UN_STATISTICAL_REGION 
FOREIGN KEY(STATISTICAL_REGIONID) REFERENCES 
UN_STATISTICAL_REGION (STATISTICAL_REGIONID)
GO

ALTER TABLE COUNTRY CHECK CONSTRAINT FK_COUNTRY_UN_STATISTICAL_REGION
GO

ALTER TABLE POPULATION  WITH CHECK ADD  CONSTRAINT FK_POPULATION_COUNTRY 
FOREIGN KEY(COUNTRYID) 
REFERENCES COUNTRY (COUNTRYID)
GO

ALTER TABLE POPULATION CHECK CONSTRAINT FK_POPULATION_COUNTRY
GO

ALTER TABLE POPULATION  WITH CHECK ADD  CONSTRAINT FK_POPULATION_YEAR_DOMAIN 
FOREIGN KEY(YEARID) REFERENCES YEAR_DOMAIN (YEARID)
GO

ALTER TABLE POPULATION CHECK CONSTRAINT FK_POPULATION_YEAR_DOMAIN
GO

These scripts will create five tables. COUNTRY, UN_CONTINENTAL_REGION,POPULATION,YEAR_DOMAIN and UN_STATISTICAL_REGION.

Probably overkill, but this is for illustration of multiple technologies in a very simple application.

Need some data scripts to populate the database:

INSERT INTO YEAR_DOMAIN
(YEARID) VALUES(2015);

INSERT INTO YEAR_DOMAIN
(YEARID) VALUES(2016);

INSERT INTO YEAR_DOMAIN
(YEARID) VALUES(2017);

INSERT INTO UN_CONTINENTAL_REGION
(CONTINENTIAL_REGION) VALUES('ASIA');

INSERT INTO UN_CONTINENTAL_REGION
(CONTINENTIAL_REGION) VALUES('AMERICAS');

INSERT INTO UN_CONTINENTAL_REGION
(CONTINENTIAL_REGION) VALUES('AFRICA');

INSERT INTO UN_CONTINENTAL_REGION
(CONTINENTIAL_REGION) VALUES('EUROPE');

INSERT INTO UN_CONTINENTAL_REGION
(CONTINENTIAL_REGION) VALUES('OCEANIA');

INSERT INTO UN_STATISTICAL_REGION
(STATISTICAL_REGION) VALUES('Eastern Asia')

INSERT INTO UN_STATISTICAL_REGION
(STATISTICAL_REGION) VALUES('Southern Asia')

INSERT INTO UN_STATISTICAL_REGION
(STATISTICAL_REGION) VALUES('Northern America')

INSERT INTO UN_STATISTICAL_REGION
(STATISTICAL_REGION) VALUES('South-Eastern Asia')

INSERT INTO UN_STATISTICAL_REGION
(STATISTICAL_REGION) VALUES('South America')

INSERT INTO UN_STATISTICAL_REGION
(STATISTICAL_REGION) VALUES('Australia and New Zealand')

DECLARE @CNREGID INT, @STATREGID INT
SELECT @CNREGID=CONTINENTIAL_REGIONID FROM 
    UN_CONTINENTAL_REGION WHERE CONTINENTIAL_REGION='ASIA'
SELECT @STATREGID=STATISTICAL_REGIONID FROM 
    UN_STATISTICAL_REGION WHERE STATISTICAL_REGION='Eastern Asia'

INSERT INTO COUNTRY
(COUNTRY, CONTINENTIAL_REGIONID,STATISTICAL_REGIONID)
VALUES('CHINA',@CNREGID,@STATREGID)

SELECT @STATREGID=STATISTICAL_REGIONID FROM 
    UN_STATISTICAL_REGION WHERE STATISTICAL_REGION='Southern Asia'

INSERT INTO COUNTRY
(COUNTRY, CONTINENTIAL_REGIONID,STATISTICAL_REGIONID)
VALUES('INDIA',@CNREGID,@STATREGID)

SELECT @STATREGID=STATISTICAL_REGIONID FROM 
    UN_STATISTICAL_REGION WHERE STATISTICAL_REGION='South-Eastern Asia'

INSERT INTO COUNTRY
(COUNTRY, CONTINENTIAL_REGIONID,STATISTICAL_REGIONID)
VALUES('INDONESIA',@CNREGID,@STATREGID)

SELECT @CNREGID=CONTINENTIAL_REGIONID FROM 
    UN_CONTINENTAL_REGION WHERE CONTINENTIAL_REGION='AMERICAS'
SELECT @STATREGID=STATISTICAL_REGIONID FROM 
    UN_STATISTICAL_REGION WHERE STATISTICAL_REGION='Northern America'

INSERT INTO COUNTRY
(COUNTRY, CONTINENTIAL_REGIONID,STATISTICAL_REGIONID)
VALUES('UNITED STATES',@CNREGID,@STATREGID)

SELECT @STATREGID=STATISTICAL_REGIONID FROM 
    UN_STATISTICAL_REGION WHERE STATISTICAL_REGION='South America'

INSERT INTO COUNTRY
(COUNTRY, CONTINENTIAL_REGIONID,STATISTICAL_REGIONID)
VALUES('BRAZIL',@CNREGID,@STATREGID)


SELECT @CNREGID=CONTINENTIAL_REGIONID FROM 
    UN_CONTINENTAL_REGION WHERE CONTINENTIAL_REGION='OCEANIA'
SELECT @STATREGID=STATISTICAL_REGIONID FROM 
    UN_STATISTICAL_REGION WHERE STATISTICAL_REGION='Australia and New Zealand'

INSERT INTO COUNTRY
(COUNTRY, CONTINENTIAL_REGIONID,STATISTICAL_REGIONID)
VALUES('AUSTRALIA',@CNREGID,@STATREGID)

DECLARE @COUNTRYID INT;
SELECT @COUNTRYID=COUNTRYID FROM COUNTRY WHERE COUNTRY='CHINA'
INSERT INTO dbo.[POPULATION] (COUNTRYID,YEARID,POPULATION)
VALUES(@COUNTRYID,2015,1376048943)

INSERT INTO dbo.[POPULATION] (COUNTRYID,YEARID,POPULATION)
VALUES(@COUNTRYID,2016,1382323332)

SELECT @COUNTRYID=COUNTRYID FROM COUNTRY WHERE COUNTRY='INDIA'
INSERT INTO dbo.[POPULATION] (COUNTRYID,YEARID,POPULATION)
VALUES(@COUNTRYID,2015,1311050527)

INSERT INTO dbo.[POPULATION] (COUNTRYID,YEARID,POPULATION)
VALUES(@COUNTRYID,2016,1326801576)

SELECT @COUNTRYID=COUNTRYID FROM COUNTRY WHERE COUNTRY='UNITED STATES'
INSERT INTO dbo.[POPULATION] (COUNTRYID,YEARID,POPULATION)
VALUES(@COUNTRYID,2015,321773631)

INSERT INTO dbo.[POPULATION] (COUNTRYID,YEARID,POPULATION)
VALUES(@COUNTRYID,2016,324118787)


SELECT @COUNTRYID=COUNTRYID FROM COUNTRY WHERE COUNTRY='INDONESIA'
INSERT INTO dbo.[POPULATION] (COUNTRYID,YEARID,POPULATION)
VALUES(@COUNTRYID,2015,257563815)

INSERT INTO dbo.[POPULATION] (COUNTRYID,YEARID,POPULATION)
VALUES(@COUNTRYID,2016,260581100)

SELECT @COUNTRYID=COUNTRYID FROM COUNTRY WHERE COUNTRY='BRAZIL'
INSERT INTO dbo.[POPULATION] (COUNTRYID,YEARID,POPULATION)
VALUES(@COUNTRYID,2015,207847528)

INSERT INTO dbo.[POPULATION] (COUNTRYID,YEARID,POPULATION)
VALUES(@COUNTRYID,2016,209567920)

SELECT @COUNTRYID=COUNTRYID FROM COUNTRY WHERE COUNTRY='AUSTRALIA'
INSERT INTO dbo.[POPULATION] (COUNTRYID,YEARID,POPULATION)
VALUES(@COUNTRYID,2015,23968973)

INSERT INTO dbo.[POPULATION] (COUNTRYID,YEARID,POPULATION)
VALUES(@COUNTRYID,2016,24309330)

Scope of Step #2

This step is to get the Visual Studio Solution created.

Visual Studio

One of my colleagues loves to do his web development in JetBrains WebStorm - he swears by it, but I am a life long Visual Studio junkie. As of this writing, I am coding this post using Visual Studio 2017 Community Edition - a first for me, I usually use Professional or Enterprise, but heck, the Community version is so well loaded I love it.

Creating The Project

Open Visual Studio.

  1. Create New Project.
  2. When the dialog appears, select Web from the Installed Templates treeview. The middle list will populate, with varying options. Please select ASP.NET Web Application (.NET Framework)
  3. Select a Name, I am picking WorldPopulation along with a location to place the solution.

  4. When the next dialog pops up, among the templates - select Empty BUT look at the checkboxes below. We need to add in some CORE References. Make sure to check MVC and Web API.

  5. Do not select Add Unit Tests - we will do that but will not use the VisualStudio.TestTools.UnitTesting - rather we will use NUnit.

So, what are those other templates? Lots of tutorials simply skip explaining that. I will take a shot at explaining so you can have an understanding of what you are NOT doing at this point in time.

Note: Also see ASP.NET Web Application Projects and Web Application Projects versus Web Site Projects in Visual Studio.

The Empty template is simply what the name says, a empty web project, bare bones. This template creates an ASP.NET web application that includes a Web.config file, but no other files. Use this project template when you do not require the functionality built into the standard template.

The Web Forms template is server based web applications. All the code resides in the IIS server and gets rendered to the client. It is older technology and not used much for modern development.

According to Microsoft, it is:
Use this project template to create a web application that is based on ASP.NET Web Forms pages and that includes the following functionality. You can choose not to use any of these features when they are not required for your application.
  • A master page.
  • A cascading style sheet.
  • Login security that uses the ASP.NET membership system.
  • Ajax scripting that uses jQuery.
  • Navigation that uses a menu control.
By default, the ASP.NET Web Application project template includes the following:
  • Folders to contain membership pages, client script files, and cascading style sheet files.
  • A data folder (App_Data), which is granted permissions that allow ASP.NET to read and write to it at run time.
  • A master page (the Site.master file).
  • Web pages named Default.aspx, Contact.aspx, and About.aspx. These content pages are based on the default master.
  • A global application class (Global.asax file).
  • A Web.config file.
  • A Packages.config file.
  • For more information, see ASP.NET Web Application Projects and Web Application Projects versus Web Site Projects in Visual Studio.

The MVC template, that is Microsoft’ Model-View-Controller template. There are many incarnations of this approach, MVVM, etc., but basically the View (what you see in the web browser) is separate from the Controller (logic) and the Model (data). That is a simplistic explanation.

Use this project template to create web applications that use a model-view-controller pattern, using the ASP.NET MVC 3 release. The MVC pattern helps separate the different aspects of the application (input logic, business logic, and UI logic), while providing a loose coupling between these elements. In addition, this project template promotes test-driven development (TDD).

The Web API is what my friend Van told me once is “Microsoft’ replacement for WCF”. It is much easier to implement, lightweight, flexible. More on this later.

The Single Page Application is a design where there is a single web page that the user sees but portions of the page can load as needed.

When selecting those templates the Solution takes on a predefined structure and loads a lot of predefined files to get the developer up and running quickly. If CORE References is selected for MVC then components needed to do MVC applications are added but the structure of the solution is not populated with a lot of predefined components which a developer doesn’t necessarily need.

WorldPopulation Project

Now that the WorldPopulation (or whatever you called your own implementation of the solution) has been created, it should have one project with the same name.

That project should have 5 Folders:

  1. App_Data
  2. App_Start
  3. Controllers
  4. Models
  5. Views

The App_Start folder will have two files in it

  1. RouteConfig.cs
  2. WebApiConfig.cs

These are critical to how our application will work. More on that later.

Now we need to add two more projects to the solution.

  1. WorldPopulation.EF (for Entity Framework, or maybe Datalayer)
  2. WorldPopulation.UnitTests

Adding Projects


Add Unit Test Project

  1. In the Solution Explorer scroll to the top, select Solution WorldPopulation.
  2. Right-click the WorldPopulation solution.
  3. When the context-menu pops up, select Add.
  4. When the second context-menu expands, select New Project.
  5. In the template selector, change to Visual C#.
  6. In the middle listing of templates - select Class Library (.NET Framework)
  7. In the Name enter WorldPopulation.EF
  8. Press OK

Add Unit Test Project

  1. In the Solution Explorer scroll to the top, select Solution WorldPopulation.
  2. Right-click the WorldPopulation solution.
  3. When the context-menu pops up, select Add.
  4. When the second context-menu expands, select New Project.
  5. In the template selector, change to Visual C#.
  6. In the middle listing of templates - select Class Library (.NET Framework)
  7. In the Name enter WorldPopulation.UnitTests
  8. Press OK

NuGet Packages

For this example, I am going to use NuGet. I know that Bower and NPM has more recent updates, but for some of the non-web portions NuGet is awesome (plus I like it).

  1. In the Solution Explorer scroll to the top, select Solution WorldPopulation.
  2. Right-click the WorldPopulation solution.
  3. When the context-menu pops up, select Manage NuGet Packages for Solution.
  4. Click Browse at the top.
NuGet Package WorldPop WorldPop.EF WorldPop.UnitTests
Entity Framework X X X
NLog X X X
NLog.Config X X X
NUnit X
NUnit3TestAdapter X
AngularJS.Core X
AngularJS.Route X
bootstrap X
FontAwesome X
lodash X
jQuery X
toastr X

jQuery may already installed by other packages like Bootstrap or by the MVC references. Select jQuery in the NuGet package manager and choose the highest available version, then select update if the installed version is below thehighest available one.

When the NuGet package installation step is completed, you will see several additional folders in the WorldPopulation Project:

  1. Content
  2. Fonts
  3. Scripts

Content Folder

This folder mainly contains CSS related files, style sheets.

Fonts

The name is self-descriptive. The customized fonts provided by the Font Awesome libraries are stored within this folder.

Scripts

JavaScript files, AngularJS, Moment.JS, jQuery, etc., all are stored in this folder.

The other NuGet packages such as NLog or Entity Framework are added to the project references.

Scope of Step #3

In this step, we will create the Data Context by connecting to the database and retrieving the model into Visual Studio.

Project WorldPopulation.EF

  1. Select the WorldPopulation.EF Project.
  2. Right click the project and click Add on the popup context menu.
  3. Select New Folder, and enter Interfaces.
  4. Repeat steps 1-3, only enter Models instead of Interfaces for the new folder name this time.
  5. Select the Models folder you just created.
  6. Right-click and select Add from the first context menu, then New Item from the second.
  7. Select Data from the templates category on the left, then when it is filtered select ADO.NET Entity Data Model.
  8. For the name enter WPModel. Then click *Add.
  9. When the next dialog opens, select EF Designer from database.
  10. Click Next.
  11. In the Choose Your Data Connection dialog, enter the SQL Server connection information where you created the tables in Step #1.
  12. Let the settings be the default for the App.Config in my case, my SQL Server database was named DEV so the name of the connection string in App.Config is DEVEntities.
  13. In the next dialog, Choose Your Database Objects and Settings -we are only selecting the tables. Expand the tables, schema. Select:
TABLE NAME
COUNTRY
POPULATION
UN_CONTINENTAL_REGION
UN_STATISTICAL_REGION
YEAR_DOMAIN


Additional Options Yes/No
Pluralize or Singularize Generated Object Names Yes
Include Foreign Key Columns in the Model Yes
Import selected Stored Procedures and functions into the entity model No

MODEL NAMESPACE DEVModel

Remember DEV is my database name, so whatever you choose will be here

  1. Click Finish

Visual Studio will chug along as it reads the database, getting the model and generates the classes.

When it is done, you will see a visual representation of the SQL Server tables and their relationships in an EDMX file within the Models folder (remember how I had you select it before you started adding the ADO.NET Entity Data Model).

Under the file WPModel.tt you will see the class files for each table in the database.

Under the file WPModel.Context.tt is the WPModel.Context.cs this is the datacontext. I am not going to spend a lot of time explaining this, but you need to read more on this from the MSDN or EntityFramework Tutorial


Scope of Step #4

We need to create our Repository, a tiny one.
We will be working in the WorldPopulation.EF Project. We will be implementing the Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application (9 of 10) which basically is

“The repository pattern is an abstraction. It’s purpose is to reduce complexity and make the rest of the code persistent ignorant. As a bonus it allows you to write unit tests instead of integration tests.”

Repository Pattern Resources

Steps to Pattern Implementation

  1. Select WorldPopulation.EF Project.
  2. Select the Interfaces Folder.
  3. Right click the context menu, click Add select Class.
  4. Enter IRepository.cs. Click OK.
  5. Open the file IRepository.cs. Modify class IRepository to public interface IRepository.
  6. The interface should be similar to the following:

    
    using WorldPopulation.EF.Models; 
    using System.Collections.Generic;
    namespace WorldPopulation.EF.Interfaces
    {
    public interface IRepository
    {
    List GetCountries();
    COUNTRY GetCountry(int countryID);
    }
    }

  1. Select the Models Folder.
  2. Right click the context menu, click Add select Class.
  3. Enter Repository.cs. Click OK.
  4. We are creating the Repository Pattern and implementing the Interface above in the folder Models in the file named Repository.cs.
  5. Be sure to add the following namespace reference at the beginning of the file:
  6. WorldPopulation.EF.Interfaces;
  7. Type the following code into the file Repository.cs :

using WorldPopulation.EF.Interfaces;
using System.Collections.Generic;
namespace WorldPopulation.EF.Models
{
     public class Repository : IRepository
     {
          private DEVEntities dbConn;
          public Repository()
          {
               dbConn=new DEVEntities();
          }

          public List<COUNTRY> GetCountries()
          {
               throw new NotImplementedException();
          }

          public COUNTRY GetCountry(int countryID)
          {
               throw new NotImplementedException();
          } 
     } 
}

Scope of Step #5

Now we get into the grit.

I still consider myself new to [TDD]TDD (Test-Driven Development).

Update Unit Test

  1. Add reference to WorldPopulation.EF project.
  2. Select WorldPopulation.UnitTests. Rename the file Class1.cs to DatalayerUnitTests.cs.
  3. Open the file DatalayerUnitTests.cs
  4. Add using NUnit.Framework; to class.
  5. Above the class name public class DatalayerUnitTests add the attribute [TestFixture].
  6. Create private member private IRepository repo; You will need to add a reference to the namespace using WorldPopulation.EF.Interfaces;.
  7. Create a new void method named Setup() with the attribute [Setup].
  8. Add a method named public void Setup() just below the [Setup] attribute. In this method add the following line:
    repo=new Repository(); You will need to import using WorldPopulation.EF.Models; namespace.
  9. Now create our first failing unit test.
        [Test]
        public void GetCountries_ExpectNone()
        {
            repo.GetCountries();
            Assert.That(repo!=null);
        }

NOTE This unit test as written will fail. When you open the repo.GetCountries() implementation, the code is

        public List<COUNTRY> GetCountries()
        {
            throw new NotImplementedException();
        }

Technically, we should be using a MOCK Interface for this but I am short on time so I am going to violate TDD and have the Unit Test become a Integration Test by accessing the data.

Modify the implementation as follows:

        public List<COUNTRY> GetCountries()
        {
            return dbConn.COUNTRies.ToList();
        }

Scope of Step #6

Switch to the WorldPopulation Project.

  1. Select Controllers folder.
  2. Select Add from the context-menu.
  3. Select Controller… from the second context-menu.
  4. Select Web API 2 Controller - Empty* then press Add.
  5. When the next dialog appears type WorldPopulation and the new controller name should be WorldPopulationController.
  6. Press the Add button.
  7. You should see a new file added into the folder Controllers with the name WorldPopulationController.cs.
  8. When the file opens, expand the curly braces.
  9. Create a Controller Constructor to instantiate the IRepository interface. You may need to add a reference to the WorldPopulation.EF project
        private IRepository repo;

        public WorldPopulationController()
        {
            repo=new Repository();
        }
  1. Now to create a method that will be used by the presentation layer (WEB SITE).
  2. Below the constructor, type the following attribute (type a few spaces first) [HttpGet]
  3. Below that attribute type [ActionName("GetCountries")]
  4. Now type below that attribute, type in:
        public IEnumerable<COUNTRY> GetCountries()
        {
           return repo.GetCountries();
        }

The entire function should look like:

        [HttpGet]
        [ActionName("GetCountries")]
        public IEnumerable<COUNTRY> GetCountries()
        {
           return repo.GetCountries();
        }

Scope of Step #7

Step #7 will begin to add some basic navigation components to the main web project. A HomeController a basic layout page and the default index.cshtml page.

Starting with the HomeController

Controller classes in AMVC are used to prepare the Model that will be mapped to a view for a particular resource. In this example, AMVC created the following Home Controller that will be used to return the default view for our application.

  1. Select Controllers folder.
  2. Select Add from the context-menu.
  3. Select Controller… from the second context-menu.
  4. Select MVC 5 Controller - Empty then press Add*.
  5. When the next dialog appears type Home and the new controller name should be HomeController.
  6. Press the Add button.
  7. You should see a new file added into the folder Controllers with the name HomeController.cs.
  8. Open the *HomeController.

You should see the following:

        // GET: Home
        public ActionResult Index()
        {
            return View();
        }
Now we are going to add some view to the project.
  1. Select the Views folder. Right-Click and choose the Add from the context-menu.
  2. Select MVC 5 Layout Page (Razor). In the popup, select _LayoutPage as the file name.
  3. Paste the following code into the new _LayoutPage.cshtml file:
 <!DOCTYPE html>
 <html>
 <head>
     <meta charset="utf-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - My ASP.NET Application</title>
    <link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
    <link href="~/Content/bootstrap.min.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink("World Population", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav"></ul>
            </div>
        </div>
    </div>

    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - WorldPopulation</p>
        </footer>
    </div>

    <script src="~/Scripts/jquery-3.1.1.min.js"></script>
    <script src="~/Scripts/bootstrap.min.js"></script>
</body>
</html>