Friday, August 9, 2019

ASP.NET Core 2.2 Web API application that works with Mongo database

You can do this tutorial on Linux, Mac or Windows.

In this tutorial I will show you how to develop an ASP.NET 2.2 API application that interacts with MongoDB. In order to proceed you will need the following pre-requisites:
  • Docker - used to host MongoDB. This is my preferred approach because it is quick and does not take too many resources on the computer.
  • .NET Core 2.2
  • Visual Studio Code
Source code for this tutorial can be found at https://github.com/medhatelmasry/AspMongo.
Video: https://youtu.be/TgD4qYrvBaY

The Database

Run the following command in a terminal window to start a MongoDB container named mdb:

docker run -p 22222:27017 --name mdb -d mongo:4.0.11

You can verify that the container is running by typing the following command in a terminal window:

docker ps -a

Let us start a bash session inside the container so that we can create a database and add some sample data to it. Enter the following command to start an interactive bash session inside the container:

docker exec -it mdb bash

We can then use the mongo command line interface (CLI) to create a database and then add some sample data to a collection of students. Type the following command:

mongo

This takes you into a mongo session command prompt. Enter the following command to create a database named CollegeDB:

use CollegeDB

Next, let's create a collection named students and add to it six sample students:

db.Students.insertMany(
[
  {'FirstName':'Sue','LastName':'Fox','School':'Business'},
  {'FirstName':'Tom','LastName':'Max','School':'Mining'},
  {'FirstName':'Ann','LastName':'Lee','School':'Nursing'},
  {'FirstName':'Joe','LastName':'Roy','School':'Tourism'},
  {'FirstName':'Jan','LastName':'Ash','School':'Communications'},
  {'FirstName':'Eva','LastName':'Day','School':'Medicine'},
]);

You can retrieve the list of students with the following command:

db.Students.find({}).pretty();

To exit the mongo command prompt by typing:

CTRL+C

You can also exit the bash session and return to the host operating system by typing:

exit

Creating an ASP.NET 2.2 MVC application

Find out what versions of .NET Core SDKs you have on your computer by running this command in a terminal windows:

dotnet --list-sdks

You will see a list like this:

2.1.504 [C:\Program Files\dotnet\sdk]
2.1.505 [C:\Program Files\dotnet\sdk]
2.1.602 [C:\Program Files\dotnet\sdk]
2.1.700 [C:\Program Files\dotnet\sdk]
2.1.800-preview-009677 [C:\Program Files\dotnet\sdk]
2.2.104 [C:\Program Files\dotnet\sdk]
3.0.100-preview6-012264 [C:\Program Files\dotnet\sdk]

I chose to use the exact version 2.2.104.

Choose a suitable workspace on your computer then create a directory named AspMongo. While in a terminal window inside of the AspMongo directory, add a global.json file that targets ASP.NET Core 2.2 with the following command:

dotnet new globaljson --sdk-version 2.2.104 

The above command generated a global.json file that looks like this:
{
 "sdk": {
    "version": "2.2.104"
  }
}
The next step is to create an ASP.NET 2.2 MVC template application. This is accomplished by entering this command:

dotnet new mvc

Let us see what our application looks like. Run the application by executing:

dotnet run

The above command builds and runs the application in a web server. Point your browser to https://localhosy:5001. This is what you should see:


Stop the web server by hitting CTRL+C on the keyboard.

Building the Students API

Find the latest version of MongoDB driver from https://www.nuget.org/packages/MongoDB.Driver/. At the time of writing, the latest version was 2.8.1. Add the MongoDB driver Nuget package with the following command:

dotnet add package MongoDB.Driver --version 2.8.1

Now that we have the driver, we can proceed with coding our API. I will use Visual Studio Code because it is operating system agnostic. To load your application workspace into VS Code, simply type in the following command from the root folder of your application:

code .

We need a Student class to represent the schema for student documents in the Students collection in MongoDB. Inside the Models folder, add a file named Student.cs with the following code:

using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace AspMongo.Models {
  public class Student {
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string StudentId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    [BsonElement("School")]
    public string Department { get; set; }
  }
}

In the preceding class, the StudentId property:
  • Is required for mapping the Common Language Runtime (CLR) object to the MongoDB collection.
  • Is annotated with [BsonId] to designate this property as the document's primary key.
  • Is annotated with [BsonRepresentation(BsonType.ObjectId)] to allow passing the parameter as type string instead of an ObjectId structure. Mongo handles the conversion from string to ObjectId.
The Department property is annotated with the [BsonElement] attribute. The attribute's value of School represents the property name in the MongoDB collection.
Add the following to appsettings.json:

"StudentDatabaseSettings": {
  "StudentsCollectionName": "Students",
  "ConnectionString": "mongodb://localhost:22222",
  "DatabaseName": "CollegeDB"
},

The entire contents of appsettings.json will look like this:

{
  "StudentDatabaseSettings": {
    "StudentsCollectionName": "Students",
    "ConnectionString": "mongodb://localhost:22222",
    "DatabaseName": "CollegeDB"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}
Add StudentDatabaseSettings.cs to the Models folder with this code:

namespace AspMongo.Models {
  public class StudentDatabaseSettings : IStudentDatabaseSettings {
      public string StudentsCollectionName { get; set; }
      public string ConnectionString { get; set; }
      public string DatabaseName { get; set; }
  }
  public interface IStudentDatabaseSettings {
      string StudentsCollectionName { get; set; }
      string ConnectionString { get; set; }
      string DatabaseName { get; set; }
  }   
}

The above StudentDatabaseSettings class is used to store the appsettings.json file's StudentDatabaseSettings property values. The JSON and C# property names are named identically to ease the mapping process.

Add the following code to Startup.ConfigureServices():

services.Configure<StudentDatabaseSettings>(
  Configuration.GetSection(nameof(StudentDatabaseSettings)));

services.AddSingleton<IStudentDatabaseSettings>(sp =>
  sp.GetRequiredService<IOptions<StudentDatabaseSettings>>().Value);
In the above code:
  • The configuration instance to which the appsettings.json file's StudentDatabaseSettings section binds is registered in the Dependency Injection (DI) container.
  • The IStudentDatabaseSettings interface is registered in DI with a singleton service lifetime. When injected, the interface instance resolves to a StudentDatabaseSettings object.
Create a folder named Services and add to it a file named Studentservice.cs with the following code:

using System.Collections.Generic;
using AspMongo.Models;
using MongoDB.Driver;
namespace AspMongo.Services {
  public class StudentService {
    private readonly IMongoCollection<Student> _Students;
    public StudentService(IStudentDatabaseSettings settings) {
      var client = new MongoClient(settings.ConnectionString);
      var database = client.GetDatabase(settings.DatabaseName);
      _Students = database.GetCollection<Student>(settings.StudentsCollectionName);
    }
    public List<Student> Get() =>
      _Students.Find(student => true).ToList();
    public Student Get(string id) =>
      _Students.Find<Student>(student => student.StudentId == id).FirstOrDefault();
    public Student Create(Student student) {
      _Students.InsertOne(student);
      return student;
    }
    public void Update(string id, Student studentIn) =>
      _Students.ReplaceOne(student => student.StudentId == id, studentIn);
    public void Remove(Student studentIn) =>
      _Students.DeleteOne(student => student.StudentId == studentIn.StudentId);
    public void Remove(string id) =>
      _Students.DeleteOne(student => student.StudentId == id);
  }
}

In the above code, an IStudentDatabaseSettings instance is retrieved from DI via constructor injection. Also, the above class knows how to connect to the MongoDB server and use the available driver methods to retrieve, insert, update and delete data.

To register the StudentService class with DI to support constructor injection, add the following to Startup.ConfigureServices():

services.AddSingleton<StudentService>();

Add a StudentsController.cs class to the Controllers directory with the following code:

using System.Collections.Generic;
using AspMongo.Models;
using AspMongo.Services;
using Microsoft.AspNetCore.Mvc;
namespace AspMongo.Controllers {
  [Route("api/[controller]")]
  [ApiController]
  public class StudentsController : ControllerBase {
      private readonly StudentService _StudentService;
      public StudentsController(StudentService studentService) {
          _StudentService = studentService;
      }
      [HttpGet]
      public ActionResult<List<Student>> Get() =>
          _StudentService.Get();
      [HttpGet("{id:length(24)}", Name = "GetStudent")]
      public ActionResult<Student> Get(string id) {
          var student = _StudentService.Get(id);
          if (student == null)
              return NotFound();
          return student;
      }
      [HttpPost]
      public ActionResult<Student> Create(Student student) {
          _StudentService.Create(student);
          return CreatedAtRoute("GetStudent", new { id = student.StudentId.ToString() }, student);
      }
      [HttpPut("{id:length(24)}")]
      public IActionResult Update(string id, Student studentIn) {
          var student = _StudentService.Get(id);
          if (student == null)
              return NotFound();
          _StudentService.Update(id, studentIn);
          return NoContent();
      }
      [HttpDelete("{id:length(24)}")]
      public IActionResult Delete(string id) {
          var student = _StudentService.Get(id);
          if (student == null)
              return NotFound();
          _StudentService.Remove(student.StudentId);
          return NoContent();
      }
  }
}

The StudentsController class:
  • Uses the StudentService class to perform CRUD operations.
  • Contains action methods to support GET, POST, PUT, and DELETE HTTP requests.
  • Calls CreatedAtRoute in the Create action method to return an HTTP 201 response.

Running the application

Start the application by executing the this command from a terminal windows at the root of the application:

dotnet run

You can view the data by pointing your browser to https://localhost:5001/api/Students. This is the expected output:

Go ahead and test the other API endpoints for POST, PUT and DELETE using your favorite tool like Postman or curl. It should all work.

I hope you learned something new in this tutorial, Until next time, happy coding.