.NET Core and KnockoutJS web application

From all different solutions for various problems I’ve implemented in the past I wanted to create a simple web application that I could get back to for reference any time, and this would be a fully working KnockoutJS web application. The idea was to implement a simple client contacts management application using .NET Core 1.1, Entity Framework Core 1.1, KnockoutJS, and MSTest + Jasmine for unit testing.

Please find the source code of the KnockoutJS web application I will be talking about in my GitHub. It’s a good idea to download/checkout the source code before continuing to read further.

I’ve started with a default .NET Core scaffold application and deleted most of the stuff from Layout and Views. After implementing the app I had three main views, one controller for KnockoutJS routing, and two controllers for RESTful WebAPI. Screenshot below.

KnockoutJS web application Solution View

Next is database. Connection string is stored in appsettings.json file, and database context for EF is wired in DI in ConfigureServices method in the Startup class.

services.AddDbContext<AppDbContext>(o => o.UseSqlServer(_configuration.GetConnectionString("AppDbContext")));

Initial data is seeded by SeedTestData() method which calls EnsureSeedData() extension method. For production systems seeding data might be an optional step. A sample migration is included in the Migrations folder.

I stored models in the Models folder, and used DataAnnotations attributes for the validation. This simplifies the validation process in the controller by letting to use ModelState.IsValid property. And for easier client status management and validation I used an enum. See this blog post for more details about the implementation.

To display error pages I used custom error pages (view + controller actions), more details here. And to log errors used .NET Core ILogger interface and NLog wrapper. NLog configured in nlog.config to filter user logs, and all the framework info and debug information. To enable NLog in your application simply add following to Configure method in the Startup class.

loggerFactory.AddNLog();
loggerFactory.ConfigureNLog("nlog.config");

RESTful WebAPI has following endpoints that could be tested using Swagger.

KnockoutJS web application REST endpoints

UI implemented using KnockoutJS framework using components to keep Views cleaner, and JavaScript services to isolate code that could be reusable. UI validation uses Knockout Validation library. RequireJS used for dependency injection to create instances of components and services automatically. RequireJS config is in the _Layout.cshtml file. See below.

var basePath = '@Url.Content("~/")';
require.config({
    baseUrl: basePath + "app",
    shim: {
        "bootstrap": { "deps": ['jquery'] },
        "knockout-validation": { "deps": ['knockout'] }
    },
    paths: {
        'jquery': basePath + 'lib/jquery/dist/jquery',
        'knockout': basePath + 'lib/knockout/dist/knockout',
        'text': basePath + 'lib/text/text',
        'bootstrap': basePath + 'lib/bootstrap/dist/js/bootstrap',
        'knockout-validation': basePath + 'lib/knockout-validation/dist/knockout.validation'
    }
});

Unit testing for back-end code logic implemented using MSTest and Moq for controllers and helpers. To mock database I used in-memory database and it makes tests much more realistic than just simply mocking DbSet objects. Once in-memory database nuget package installed, it’s very easy to create a databaset context that uses in-memory database. Following is approach to have unique test databases created before each test.

private DbContextOptions _dbContextOptions;

[TestInitialize]
public void Init()
{
    _dbContextOptions = new DbContextOptionsBuilder<AppDbContext>()
        .UseInMemoryDatabase(Guid.NewGuid().ToString())
        .Options;

    // Make sure each test gets a clean database
    using (var context = new AppDbContext(_dbContextOptions))
    {
        context.Database.EnsureDeleted();
    }
}

UI (JavaScript) unit tests done using Jasmine. More details about services mocking is in this blog post. To enable Jasmine to work with RequireJS configuration was set up in require.config.js, and Chutzpah used to run tests with a config in chutzpah.json file. Following Visual Studio extensions make life easier to run tests from the Visual Studio: Chutzpah Test Runner Context Menu Extension and Chutzpah Test Adapter for the Test Explorer.

And the running KnockoutJS web application looks like in the following screenshots. Give it a go!

KnockoutJS web application Main List
KnockoutJS web application Edit View

Some more technical details can be found in the readme file on GitHub.

Update 28/01/2019

Upgraded to the latest .NET Core 1.x and other nuget packages. You might need to install the latest 1.x SDK to be able to run the solution.