Thursday, 27 December 2018

SOLID REST API using ASP.NET Core

The following article shows how to implementing SOLID REST API using ASP.NET Core. The solution uses generic repository pattern to perform CRUD operations on database, and also xUnit as test runner.
The solution will contain three key namespace:
  • Data access: it will implement information about the domain model and the relationship between entities. It will also contain the information about data context;
  • Domain logic: it will implement the repositories and services used by our web APIs;
  • API: it will implement controllers and middlewares to manage incoming requests;
The project is available on Github.
Project overview
Here is a brief schema of the project structure:
Implementing SOLID REST API using ASP.NET Core

The Blog.Turnmeup.DAL  represents the Data access namespace, the Blog.Turnmeup.DLrepresents the Domain Logic namespace, finally the Blog.Turnmeup.API exposes Rest APIs.
Project Testing
The key parts of the solution are convered by unit tests and integration tests. Test projects will use Moq as mocking library and xUnit as test runner. All tests will be contained in two namespaces:
  • DL.Tests: it will contain domain logic tests;
  • API.Tests: it will contain APIs tests;

Data access using Entity Framework

Data access layer uses Entity Framework Core as ORM.
The project (Blog.Turnmeup.DAL) contains entity classes which describe model data. The namespace also defines  BaseEntity model class which contains some common attributes. BaseEntity is extended by the other entity classes. Domain Logic namespace references models  to retrieve data.
Here is a schema of the project:
Implementing SOLID REST API using ASP.NET Core
In order to generate the database schema you need to run the following command inside the directory of the project:
dotnet ef database update

Domain Logic layer overview

This project (Blog.Turnmeup.DL) implements the Domain logic layer. It uses the Generic repository pattern combined with a generic service class. Here is an overview of the project:
Implementing SOLID REST API using ASP.NET Core
Unit testing
The Domain logic layer is covered by unit tests. The project Blog.Turnmeup.DL.Testsdefines the unit tests inside the CourseServiceTests class. Here is the source code of tests:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Blog.Turnmeup.DL.Repositories;
using Blog.Turnmeup.DL.Services;
using Blog.Turnmeup.Models;
using Moq;
using Xunit;
namespace Blog.Turnmeup.DL.Tests.Services
{
public class CourseServiceTest
{
private Mock<IBaseRepository<Course>> Repository { get; }
private IBaseService<Course> Service { get; }
public CourseServiceTest()
{
var entity = new List<Course>
{
new Course
{
Id = 1,
Title = "Test",
Url = "https://samueleresca.net/",
DateAdded = DateTime.Now
}
};
Repository = new Mock<IBaseRepository<Course>>();
Repository.Setup(x => x.GetAll())
.ReturnsAsync(entity);
Repository.Setup(x => x.GetById(It.IsAny<int>()))
.Returns((int id) => Task.Run(() => entity.Find(s => s.Id == id)));
Repository.Setup(x => x.Where(It.IsAny<Expression<Func<Course,bool>>>()))
.Returns((Expression<Func<Course, bool>> exp) => entity.AsQueryable().Where(exp));
Repository.Setup(x => x.Insert(It.IsAny<Course>()))
.Callback((Course label) => entity.Add(label));
Repository.Setup(x => x.Update(It.IsAny<Course>()))
.Callback((Course label) => entity[entity.FindIndex(x => x.Id == label.Id)] = label);
Repository.Setup(x => x.Delete(It.IsAny<Course>()))
.Callback((Course label) => entity.RemoveAt(entity.FindIndex(x => x.Id == label.Id)));
Service = new BaseService<Course>(Repository.Object);
}
[Fact]
public void Can_Get_All()
{
// Act
var entities = Service.Get().Result;
// Assert
Repository.Verify(x => x.GetAll(), Times.Once);
Assert.Equal(1, entities.Count());
}
[Fact]
public void Can_Get_Single()
{
// Arrange
var testId = 1;
// Act
var l = Service.GetById(testId).Result;
// Assert
Repository.Verify(x => x.GetById(testId), Times.Once);
Assert.Equal("Test", l.Title);
Assert.Equal("https://samueleresca.net/", l.Url);
}
[Fact]
public void Can_Filter_Entities()
{
// Arrange
var courseId = 1;
// Act
var filteredEntities = Service.Where(s=> s.Id == courseId).First();
// Assert
Repository.Verify(x => x.Where(s => s.Id == courseId), Times.Once);
Assert.Equal(courseId, filteredEntities.Id);
Assert.Equal("Test", filteredEntities.Title);
}
[Fact]
public void Can_Insert_Entity()
{
// Arrange
var entity = new Course
{
Id = 2,
Title="Course 2",
DateAdded = DateTime.MaxValue
};
// Act
Service.AddOrUpdate(entity);
// Assert
Repository.Verify(x => x.GetById(It.IsAny<int>()), Times.Once);
Repository.Verify(x => x.Insert(It.IsAny<Course>()), Times.Once);
var entities = Service.Get().Result;
Assert.Equal(2, entities.Count());
}
[Fact]
public void Can_Update_Entity()
{
// Arrange
var entity = new Course
{
Id = 1,
Title = "Course 2 ",
Url = "https://samueleresca.net"
};
// Act
Service.AddOrUpdate(entity);
// Assert
Repository.Verify(x => x.GetById(It.IsAny<int>()), Times.Once);
Repository.Verify(x => x.Update(It.IsAny<Course>()), Times.Once);
var entityResult = Service.GetById(1).Result;
Assert.Equal("Course 2 ", entityResult.Title);
Assert.Equal(DateTime.UtcNow.Date, entityResult.DateModified.Date);
}
}
}
Model definition
The Domain Logic layer also defines BaseResponseModel.
Blog.Turnmeup.API uses BaseResponseModel to serialize data throughs HTTP response. The Blog.Turnmeup.API will useAutomapper to map Data access layer models with Domain logic models.

API namespace overview

The Blog.Turnmeup.API defines the Controller class which returns data through HTTP. The CourseController handles incoming HTTP requests and call Domain logic layer to retrieve or update data. Here is an schema overview of the project:
Implementing SOLID REST API using ASP.NET Core
Dependency injection and class mapping
The Blog.Turnmeup.API project uses dependency injection to inject concrete classes to interfaces. It also uses Automapper package to map Blog.Turnmeup.DAL to Blog.Turnmeup.DL response models. Dependency injection is managed in the Startup.cs file and classes mapping is managed by the Infrastructure.Mapper class:
using AutoMapper;
using Blog.Turnmeup.DL.Models;
using Blog.Turnmeup.Models;
namespace Blog.Turnmeup.API.Infrastructure
{
public class MappingProfile : Profile
{
public MappingProfile()
{
// Add as many of these lines as you need to map your objects
CreateMap<Course, CourseResponseModel>();
CreateMap<CourseResponseModel, Course>();
}
}
}
Unit testing
The Rest APIs layer is covered by unit tests. The project Blog.Turnmeup.API.Testsdefines the unit tests inside the CourseControllerTests class. Tests use the TextFixtureclass to initialize a Test server and retrieve the dependencies services:
using System;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
namespace Blog.Turnmeup.API.Tests
{
public class TestFixture<TStartup> : IDisposable where TStartup : class
{
public readonly TestServer Server;
private readonly HttpClient Client;
public TestFixture()
{
var builder = new WebHostBuilder()
.UseContentRoot($"..\\..\\..\\..\\..\\src\\Blog.Turnmeup.API\\")
.UseStartup<TStartup>();
Server = new TestServer(builder);
Client = new HttpClient();
}
public void Dispose()
{
Client.Dispose();
Server.Dispose();
}
}
}

Final thought

You can find the code on Github.
The project can be extended with other model classes and other controller. In the next article will be dicovered the authentication part by using ASP.NET Core Identity:

No comments:

Post a Comment

The best ways to connect to the server using Angular CLI

Everybody who has used  Angular CLI  knows that it is a powerful tool which can take a front-end development job to a completely dif...