C17 EngineeringC17 Engineering
C17 Engineering
Entreprise de Service Numérique spécialisée en technologies .NET, Cloud et DevOPS
  • Qui sommes-nous ?
    • Notre histoire
    • Chiffres clés
    • Équipe
    • Nos valeurs
  • Nos expertises
    • Nos compétences
    • Exemples de missions
  • Nos formations
    • Blockchain Masterclass
    • TopCode
  • Nos publications
    • Nos livres blancs
    • Nos vidéos
      • Les tutos
      • Les conférences
  • Rejoignez-nous !
    • Une carrière chez C17e
    • Offres d’emploi
    • Politique RH
  • Blog
Menu back  
25/11/2016Leave a commentBlog, C17ArticlesBy Pierre Murasso

A simple full stack application with .NET Core and Angular JS

  1. Intro
  2. Project Structure
  3. Code First with Entity Framework Core
  4. Dependency Injection
  5. Authentication with Identity Core
  6. API (MVC) Controllers & Angular
  7. Unit tests
  8. Building a delivery package with grunt
  9. Conclusion

Intro

Now that .NET Core 1.0 has been released, we have the opportunity to start building real world / production ready apps that target cheaper hosting environments: docker+linux, linux and any free database engine.

Third party open source libraries support is however still limited on .NET Core, and Entity Framework Core has still some drawbacks compared to Entity Framework 6. At this moment I would then consider using .NET Core either for micro services, or for quite simple web applications.

Currently, the trend is strongly in favor of micro services, so one can consider developing parts of an app using .NET Core, and make these components easily scalable either with docker or with Azure Service Fabric. There are probably other alternatives (on AWS for instance), but I’m not an expert.

For this tutorial, I built a .NET Core backend API, using ASP.NET MVC Core, Entity Framework Core, and SQL Server, and a client side SPA using Angular JS.

It is also intended to be a demo of a couple of good practices, that I myself apply to the projects I work on: layers separation with dependency injection and interfaces, testability and scripting for recurring common tasks.

All application sources, ready to run, available on Github:

https://github.com/pierrus/ngCooking-api

Clone it, and run it following instructions in the git readme.

Project structure

The new project can be created either from VS 2015, or the command line with the “dotnet new -t web” command. The files and directory structure then looks like:

Here is an overview of the core files.

Project.json

The soon to be replaced project file: will disappear with .NET Core 1.1, and will be replaced by the old style csproj XML file.

What have we got in this file ?

dependencies: you can either update this section from command line in nuget package manager (install-package / uninstall-package) or manually edit the dependencies section in the project.json file. Adding references in this section automatically triggers a package restore from nuget.

buildoptions: files to exclude from compilation (wwwroot folder), and the important emitEntryPoint which will add a directive in the output dll, pointing to the main method, making it runnable from the command line.

frameworks: the targeted frameworks. As of now, I don’t fully understand if I should exhaustively list all target frameworks? For instance, I want my app to be runnable on netcoreapp1.0 and netcoreapp1.1: should I list both of them, or the former is enough to make it runnable on netcoreapp1.1 systems ?

runtimes: if you want to make the app self-contained, add this section (which I didn’t do). If you don’t, you must install .NET Core on the target machine.

publishOptions: what to include and exclude from the published application, when using “dotnet publish”.

Program.cs

Every .NET Core project is a console application, even web apps. Look at the program.cs file. It contains a classical void Main(string[] args) method with the following code:

var host = new WebHostBuilder()
    .UseKestrel()
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseIISIntegration()
    .UseStartup()
    .Build();
host.Run();

It basically builds a new webhost, enables IIS integration, and specifies that Startup is the class called at application startup for services configuration, DI and set global configuration variables, and starts the Kestrel lightweight application server.

Startup.cs

So Startup.cs is more about configuration, at application startup.

In the constructor, are defined: the JSON configuration files, for app configuration data (connectionstrings, key/values, configuration sections. Note that the constructor gets passed an instance of the IHostingEnvironment interface. It contains a couple of info:

  • webroot path: web application client side static files
  • contentrootpat: the overall application root
  • info about debug, staging, release status (booleans)
  • WebRootFileProvider: an implementation of IFileProvider

Code First with Entity Framework Core

I chose a code first approach, in order to create the database model used for storing this application data, and manage users authentication. What does it mean ? In a few words, it means that one start by designing its object models, and based on them and their relationships, the database model is created.

Required dependencies

Let’s start by adding the required references in project.json. You can also add them from the Nuget Command ligne with “install-package <package-name>”:

  • “Microsoft.EntityFrameworkCore”: “1.0.0”
  • “Microsoft.EntityFrameworkCore.SqlServer”: “1.0.0”
  • “Microsoft.EntityFrameworkCore.Tools”: “1.0.0-preview2-final”

As I plan to identify and authenticate users, and store users’ data in the repository (the database), I chose to add Asp.Net Identity. Additional references are therefore:

  • “Microsoft.AspNetCore.Identity”: “1.0.0”
  • “Microsoft.AspNetCore.Identity.EntityFrameworkCore”: “1.0.0”

Domain objects

I then designed the domain objects as simple data transfer objects. Please check the Models directory. They are just simple DTOs, except the User class which extends IdentityUser<Int32> with additional information. IdentityUser<Int32> is an Identity User class (email, username, password, roles…) with an Int32 as its primary key (instead of a string by default).

It’s interesting to see how easy it is to extend the Asp.Net Identity model (IdentityUser<>, IdentityRole<>…) and deploy it to the database using Entity Framework Core extensions for Identity. Simply inherit IdentityUser<Int32> (I want an int primary key), add any property required as follow:

public class User : IdentityUser<Int32>
{
    [JsonProperty("firstname")]
    public String FirstName { get; set; }

    [JsonProperty("surname")]
    public String LastName { get; set; }

    [JsonProperty("level")]
    public Int32 Level { get; set; }

    ...
}

The table deployed to SQL Server, after migration deployment, will then look like:

Both classes are merged in a single table, making it very easy to extend the Identity model.

Have a look at the domain objects here: https://github.com/pierrus/ngCooking-api/tree/master/src/apis/Models

EF Context and repository pattern

NgContext

The Entity Framework context is the entry point to the underlying data store (i.e. like the session in Nhibernate). As we also persist Identity data in the database, I made the app specific context class NgContext, derive from:

IdentityDbContext<Models.User, IdentityRole<Int32>, Int32>

Specific as generic type arguments are:

  • The domain model for user: Model.Users
  • The type of the role class and its primary key type IdentityRole<Int32>
  • Int32 as the primary key used in the users tables

Check the full NGContext sources: https://github.com/pierrus/ngCooking-api/blob/master/src/apis/Data/NgContext.cs

Model customization

Sometimes it’s useful to give hints to Entity Framework, to help it deploy the database model as we want it. It’s done overriding the OnModelCreating method, inside the NgContext class. Method signature is:

protected override void OnModelCreating(ModelBuilder builder)

Things that can be customized are:

  • intermediate tables, in case of a many-to-many relationship. Entity Framework Core doesn’t support yet implicit intermediate tables, and you’ll need to create a dedicated domain object (i.e. IngredientRecette which links Ingredients to Recipes)
  • properties to ignore in the domain model (i.e. don’t create a column for these): .Ignore(r => r.Ingredients)
  • foreign key names

Repository pattern

To make things more testable, once I’ll add unit testing, I chose to avoid tight coupling between upper layer components (services, controllers) and Entity Framework components like DBContext, DBSet. DBContext and DBSet are tightly coupled to the database, and for testability it’s better to rely on more generic components like IQueryAble<T>.

public class Repository<TModel> : IRepository<TModel> where TModel:class
{
    DbSet<TModel> _set = null;
    INgContext _ngContext;

    public Repository(INgContext ngContext)
    {
        _ngContext = ngContext;
        _set = ngContext.Set<TModel>();
    }

    public void Add(TModel entity)
    {
        _set.Add(entity);
        _ngContext.SaveChanges();
    }

    public void Delete(TModel entity)
    {
        _set.Remove(entity);
        _ngContext.SaveChanges();
    }

    public IQueryable<TModel> Get()
    {
        return _set.AsQueryable<TModel>();
    }

    public void Update(TModel entity)
    {
        _set.Update(entity);
        _ngContext.SaveChanges();
    }
}

As it’s a very simple scenario, this repository suits fine for almost all domain objects. However I had to make a specific repository for one of the domain objects, with specific Entity Framework directives “Include” for related objects to enable lazy loading.

Full repository sources on Github: https://github.com/pierrus/ngCooking-api/blob/master/src/apis/Data/Repository.cs

Specific repository RecetteRepository: https://github.com/pierrus/ngCooking-api/blob/master/src/apis/Data/RecetteRepository.cs

Migrations and database updates with CLI

Once you’re done with the domain objects, the db context and the repository, next step is deploying the data model to the target database:

1. Create a migration

dotnet ef migrations add InitialDataModelMigration

2. Publish migration to the database

dotnet ef database update

Running the latter is enough, as you can find ready to deploy migrations in the project, under the migrations folder.

Doing so then results in the tables being deployed in SQL Server

Dependency Injection

Dependency injection is a pattern that brings:

  • Loose coupling between classes
  • Better and explicit management of objects’ lifecycles

Asp.Net Core comes with a builtin dependency injection container. You can replace it with more advanced containers (i.e. StructureMap, Autofac), but it seems OK for most simple scenarios. Whenever you use services.AddTransient, services.AddSingleton, or specific services methods like services.AddIdentity, it makes this services available in the default dependency injection container.

Services instances can then be injected within your app classes, through their constructors. It’s simple, straightforward, and requires very little configuration.

Services declarations, with interfaces and implementations: https://github.com/pierrus/ngCooking-api/blob/master/src/apis/Startup.cs

Want to know more about DI? Have a look here: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection

Services registration

Registering an implementation is done using the AddTransient/AddScoped/AddSingleton methods.

services.AddTransient<IRepository<Recette>, RecetteRepository>();

Available lifetimes are:

  • Transient: a new instance is created each time it is requested
  • Scoped: the instance is created on request, but its lifecycle is bound to the request
  • Singleton: one instance, bound to the application’s lifetime

Services consumption

Your classes should then declare the dependencies they require, in their constructor. For instance, a controller that relies on the repository for manipulating data should have its constructor declared in the following way:

public IngredientsController(IRepository<Ingredient> ingredientsRepository)
{
    _ingredientsRepository = ingredientsRepository;
}

https://github.com/pierrus/ngCooking-api/blob/master/src/apis/Controllers/IngredientsController.cs

Authentication with Identity Core

Adding the Identity service

I previously mentioned Identity Core. Its roles are:

  • Storing users login and roles into in the database
  • Provide a cookie based authentication for every request
  • Users login with username and password, and sets a cookie in case of success

First, let’s declare and configure the service in Startup → ConfigureServices

services.AddIdentity<Models.User, IdentityRole<Int32>>(sa =>
{
    sa.Password.RequireDigit = false;
    sa.Password.RequireUppercase = false;
    sa.Password.RequiredLength = 0;
    sa.Cookies.ApplicationCookie.LoginPath = "/App/Login";
    sa.Cookies.ApplicationCookie.ExpireTimeSpan = TimeSpan.FromDays(1);
});

Then, let’s add the middleware via the startup → configure method.

app.UseIdentity();

Identity is then added to the middlewares pipe. Also note that services order is important, and identity must be declared before the Mvc service in order to make users authentication info available in the controllers.

Please have a look at the startup file here: https://github.com/pierrus/ngCooking-api/blob/master/src/apis/Startup.cs

Declare dependencies

To inject the services, then simply declare them wherever you need, in your classes’ constructors. For instance in the AuthenticateController mvc controller class, which handles all requests related to users login, logout, and authentication status:

public AuthenticateController(UserManager<User> userManager,
           SignInManager<User> signInManager,
           IRepository<User> communityRepository)
{
    _userManager = userManager;
    _signInManager = signInManager;
    _communityRepository = communityRepository;
}

https://github.com/pierrus/ngCooking-api/blob/master/src/apis/Controllers/AuthenticateController.cs

UserManager and SigninManager are Asp.net Core Identity services. You can notice that I didn’t have to explicitly add SignInManager<T> and UserManager<T> to the DI container. In fact, using services.AddIdentity() is a shortcut for adding a set of identity implementations to the DI container:

  • UserManager
  • SignInManager
  • RoleValidator
  • PasswordValidator
  • …

https://github.com/aspnet/Identity/blob/master/src/Microsoft.AspNetCore.Identity/IdentityServiceCollectionExtensions.cs

Authenticating and getting the authentication status

Once all the previous steps are completed it’s very easy to authenticate and login. Declare the following dependencies in your controllers (or services):

  • SignInManager<User> signInManager
  • UserManager<User> userManager

Logging the user is done this way: await _signInManager.PasswordSignInAsync(email, password, true, false);

Getting the authentication status as a Boolean quite easy as well: User.Identity.IsAuthenticated

https://github.com/pierrus/ngCooking-api/blob/master/src/apis/Controllers/AuthenticateController.cs

API (MVC) Controllers & Angular

Client side

This tutorial is not specifically about angular, so I won’t delve too much into the client side code.

The structure of the project is the following one:

  • bower_components: client side dependencies (angular, angular modules, jquery)
  • css
  • fonts
  • img
  • js
    • app.js: angular main javascript entry point, with angular modules loading, custom modules loading, and routes declarations
    • controllers.js: angular controllers
    • services.js: angular lower level services, like data manipulation, and interactions with the API
  • json : flat JSON files, used in the specifications to describe the data format expected between client code and the API (should not end up in production)
  • partials: the partial views
  • bower.json: client side dependencies, for the bower package manager (should not end up in production)
  • index.html: the client side app main entry point, and the app layout

At first sight, we can see that it lacks unit tests. But anyway, this is just an example project, and what I want to look at is the js/services.js file, and more precisely the interactions with the API.

On the client side, HTTP requests are executed using the Angular $http service, whose documentation can be found here: https://docs.angularjs.org/api/ng/service/$http. The requests are pretty simple, and based on the following HTTP verbs: GET, POST, DELETE (no PUT, no PATCH).

Data basically goes in 2 different directions:

  • Client → API (request data)
  • API → Client (response data)

Client → API

Data passed as a parameter in from the browser to the API is quite simple (no complex data structure). Therefore, no JSON.

A simple GET query with no parameters is done this way:

$http.get(configService.getUrl('ingredients')).success(function(data) {
    recipesService.ingredients = data;
    deferred.resolve();
});

https://github.com/pierrus/ngCooking-api/blob/master/src/apis/wwwroot/js/services.js#L282

A simple POST query with a couple of form parameters passed in the HTTP request body is done this way:

var data = new FormData();
    data.append("rawPicture", recipe.picture);
    data.append("name", recipe.name);
    data.append("preparation", recipe.preparation);
    data.append("ingredients", ingredientsList);
    data.append("mark", 0);
    data.append("creatorId", recipe.creatorId);

$http.post(configService.getUrl('recettes'), data, config).then(
    function() { console.log('recette added OK'); },
    function() { console.log('recette added FAILED'); });

https://github.com/pierrus/ngCooking-api/blob/master/src/apis/wwwroot/js/services.js#L181

Both queries are done in async style, the GET request relies on promises (https://docs.angularjs.org/api/ng/service/$q) to get the result back to the upper layers (controllers, then view).

API → Client

On the other side, the API very often has to send back large data structures to the client, like objects or objects lists. I.e. getting a recipe, or a list of ingredients from the API.

Data is expected to be returned as JSON, and is parsed by the $http angular module, from string to object.

$http.get(configService.getUrl('recettes?id=' + recipeId)).success(function(recipe) {
    if (!recipe.comments)
        recipe.comments = new Array();

    ...
});

https://github.com/pierrus/ngCooking-api/blob/master/src/apis/wwwroot/js/services.js#L201

Server side

The entry point to the app, for the outside world, are the MVC controllers. Historically, ASP.NET MVC and ASP.NET Web API are two different things.

It’s new, and specific to .NET Core, that API and MVC controllers are developed using the same base class Microsoft.AspNetCore.Mvc.Controller.

The default format is JSON, and simply returning objects or objects lists in the MVC controllers will result in JSON being passed to the client in the HTTP requests responses.

Getting a single recipe:

var recette = _recettesRepository.Get().Where(r => r.Id == id)
    .FirstOrDefault();

recette.Calories = recette.IngredientsRecettes.Sum(i => i.Ingredient.Calories);

return recette;

Unit tests

Dependencies

I added two very simple unit tests, as a demonstration of unit testing and mocking with .NET Core.

I created the apis.test unit tests project, and added the following dependencies:

"Microsoft.NETCore.App": {
    "version": "1.0.0",
    "type": "platform"
},
"dotnet‐test‐xunit": "2.2.0‐preview2‐build1029",
"xunit": "2.2.0‐beta2‐build3300",
"apis": "1.0.0‐*",
"System.Diagnostics.TraceSource": "4.0.0",
"Moq": "4.6.38‐alpha"

https://github.com/pierrus/ngCooking-api/blob/master/src/apis.test/project.json

Mocks and unit tests

Mocks are created using the new Mock<T> pattern, ideally with an interface as the T parameter. For instance:

_repositoryMock = new Mock<IRepository<apis.Models.Recette>>();

These mocks are then injected into the components I want to unit test, in this case the MVC controller “RecettesController”, this way:

_recettesController = new apis.Controllers.RecettesController(_repositoryMock.Object, _repositoryIngredientMock.Object, _hostingEnvironment.Object);

Each test method is then decorated with the FACT attribute, and use Assert calls

https://github.com/pierrus/ngCooking-api/blob/master/src/apis.test/RecettesControllerTest.cs

Running the unit tests, and getting the results

Unit tests can then be run either from VS 2015, or the command line with the “dotnet test”.

Building a delivery package with grunt

Last part is about automating a couple of tasks. I want to:

  • Compile
  • Execute the set of unit tests
  • Concat CSS, and JS files together (2 files at total)
  • Minify the concatenated JS and CSS files
  • Update the index.html file to reference the 2 minified files
  • Make the app ready for publication on a server, by removing all unnecessary development files

I chose the GRUNT (http://gruntjs.com/) task runner. It is a javascript tool, available from the Node Package manager. After installing npm from https://www.npmjs.com/, install GRUNT with the npm install grunt-cli –save-dev and npm install grunt –save-dev commande lines.

The required grunt tasks are: grunt-contrib-concat, grunt-contrib-cssmin, grunt-contrib-uglify, grunt-exec and grunt-usemin. Install each of them with the npm install <grunt-task> –save-dev command line.

The grunt tasks are configured in the grunfile.js file (https://github.com/pierrus/ngCooking-api/blob/master/gruntfile.js).

I relied on grunt-exec to execute the unit tests, and publish the app to the ./build directory with respectively the dotnet test and dotnet publish command lines. The dotnet publish task is customized in the project.json file (https://github.com/pierrus/ngCooking-api/blob/master/src/apis/project.json): I explicitely specified which files and directories are to be included and excluded from the delivery package.

The result is not exactly a delivery package, but the app is clean and located in the ./build folder, ready to be packaged and deployed. However it misses a couple of things:

  • Configuration file for the targeted environment: an appsettings.dev.json or appsettings.prod.json should be added. But be careful about storing sensitive data on your github repository.
  • Archive: how do you plan to deploy ? FTP, SCP ? It may be useful to create a ZIP or TARBALL archive of your app.
  • If you plan to deploy in the cloud, the deployment process is very specific to the targeted environment. For instance, you’ll have to use specific tools for azure:
    • azure resource templates for describing the environment and the apps you want to deploy,
    • ms deploy to create the application package
  • Specific tasks: there are always specific tasks when an app is deployed, and the the more complex the app is, the more complex the deployment process is.

Conclusion

I hope you enjoyed this tutorial. It was meant to be a global overview of a simple app, and none of the points were discussed in details.

Please feel free to contact me on twitter (https://twitter.com/PMurasso), or via the C17 Engineering contact page.

.NET Coreangularapplicationc17full stackfullstack
Plus d'articles
C17 sur YouTube !
07/10/2019
C’est parti pour le Challenge Cooptation !
02/09/2019
La cooptation, quésaco ?
26/08/2019
RDV au dernier bar avant la fin du monde
09/08/2019
Atelier technique CQRS + Event Sourcing
06/08/2019
Mentoring meeting #2
10/07/2019
Leave Comment

Cancel 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=""> <s> <strike> <strong>

clear formSubmit

C17 Engineering
All rights reserved - @C17 ENGINEERING 2019