ASP.Net webAPI with Entity Framework Core

Ahsan Raza
Nov 4 · 8 min read

Entity Framework is an object-relational mapper (ORM) tool. With time saving auto-generated code, support of LINQ and ease in unit testing makes it natural choice of .NET developers to work with databases. Entity Framework Core or EF Core is its newest version, “lightweight, extensible, open source and cross-platform”.

In this post, I’ll share my experience with the best architecture for a web project that uses EF Core for data access. Sample provided in this ebook is huge. To explain it better, I trimmed it down for a smaller application but kept the concepts intact.

I used this architecture in many projects. As application grows bigger and more developers join the team, then one really starts appreciating it’s separation into different projects, rules and patterns. This architecture implements Repository and Service pattern, uses Generics and interfaces to have Dependency Injection and fulfils SOLID principles.

Introduction

In previous post we created an API ( Library.WebApi)

In this article we’ll use EF Core to

  • Create a database for demo Library
  • Create a business logic and data access layer
  • Create and Inject service in Library.WebApi

Prerequisites

The Architecture

We’ll have two more projects ( Class Library) in our application dealing with database using EF Core.

  • Library.Core
  • Library.Infrastructure

Library.Core

This project will hold the business logic, comprising

  • Entities
  • Interfaces
  • Services
  • Specification (Queries)

Library.Infrastructure

This is data layer. Everything to connect with database ( DbContext) to repositories will be dealt here. It’ll have

  • LibraryDBContext
  • Repositories

Create and Seed Database

1 — Clone webAPI Application

2 — Add Core Project

Add new Class Library project with name Library.Core

3 — Entities

  • Add new folder named SharedKernal
  • In this folder add new abstract class BaseEntity
  • Add folder Entities and add Book, Category, Author
  • One to Many Relationships between entities

BaseEntity

using System;using System.ComponentModel.DataAnnotations;using System.ComponentModel.DataAnnotations.Schema;namespace Library.Core.SharedKernal
{
public abstract class BaseEntity{[Key, Column(Order = 0)][DatabaseGenerated(DatabaseGeneratedOption.Identity)]public int Id { get; set; }public DateTime CreatedDate { get; set; } = DateTime.UtcNow;public DateTime? ModifiedDate { get; set; } = null;public string CreatedBy { get; set; }public string ModifiedBy { get; set; } = null;public bool IsDeleted { get; set; } = false;}
}

All entities will inherit this base entity class

4 — Infrastructure Project

5 — Add Packages

6 — Create DBContext

  • Add new folder Data to create
  • Add new class LibraryDbContext and inherit it from DbContext
using Library.Core.Entities;using Microsoft.EntityFrameworkCore;namespace Library.Infrastructure.Data
{
public class LibraryDbContext : DbContext
{
public DbSet<Author> Authors { get; set; }public DbSet<Book> Books { get; set; }public DbSet<Category> Categories { get; set; }public LibraryDbContext() { }public LibraryDbContext(DbContextOptions<LibraryDbContext> options) : base(options) { }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (optionsBuilder.IsConfigured == false)
{
var connectionString = "Server=(localdb)\\MSSQLLocalDB; Database=LibraryDb; Integrated Security=True;";optionsBuilder.UseSqlServer(connectionString);
}
}
}
}

7 — Add Initial Migration

To make changes to our database schema whenever there’s change in our model (entities), we use Entity Framework Migrations feature.

We’ll get Migrations folder automatically, in that a class created for us. We may edit it in accordance to our need.

PM> Add-Migration initial
PM> Update-Database
  • Open SQL Server Management Studio and let's see database with name LibraryDb created

8 — Seed Data

As database is created, we got all the tables we were seeking to have in it. Let’s seed database with some demo data.

  • In Library.Infrastructure Project
  • Override OnModelCreating method in LibraryDBContext class
protected override void OnModelCreating(ModelBuilder modelBuilder){modelBuilder.Entity<Author>().HasData(new Author{Id = 1,FirstName = "Elizabeth",LastName = "Gilbert"},new Author{Id = 2,FirstName = "Alex",LastName = "Michaelides"},new Author{Id = 3,FirstName = "Jayson",LastName = "Greene"},new Author{Id = 4,FirstName = "Jennifer",LastName = "Weiner"},new Author{Id = 5,FirstName = "Yangsze",LastName = "Choo"},new Author{Id = 6,FirstName = "Taylor",LastName = "Jenkins Reid"});modelBuilder.Entity<Category>().HasData(new Category { Id = 1, CategoryName = "Novel" },new Category { Id = 2, CategoryName = "Thriller" },new Category { Id = 3, CategoryName = "Memoir" });modelBuilder.Entity<Book>().HasData(new Book { Id = 1, BookName = "City of Girls", AuthorId = 1, CategoryId = 1 },new Book { Id = 2, BookName = "The Silent Patient", AuthorId = 2, CategoryId = 2 },new Book { Id = 3, BookName = "Once More We Saw Stars", AuthorId = 3, CategoryId = 3 },new Book { Id = 4, BookName = "Mrs. Everything", AuthorId = 4, CategoryId = 1 },new Book { Id = 5, BookName = "The Night Tiger", AuthorId = 5, CategoryId = 1 },new Book { Id = 6, BookName = "Daisy Jones & The Six", AuthorId = 6, CategoryId = 1 });}
  • New Migration is created
  • Update database again
PM> Add-Migration SeedData 
PM> Update-Database

Repository

Database has seeded, now we’ll fetch this data in our webAPI and further make it available for UI application to display it.

Repositories are place where we put our data access code. In this application we’ll also see the magic of generic repositories, i-e repository that takes entity as a parameter and perform data access operations on it. For further reading on Generic classes, I would recomend this.

I know, there is already a discussion in developers circles about necessity of repository pattern and sepcifically on topic that do we really need this extra layer in EF Core?

I must admit, I’m in favour of this pattern. And in this example we’ll experience a working example on how repository and service pattern can result in

  • Less repetition of code
  • Centralised management data access code
  • Allow future facilitation of caching and unit testing

And how we can have different repositories and services for our Read and Write operations to achieve maximum performance, still requiring very less code change in case of change in database technology and architecture.

Service

Service layer consumes repository and exposes business logic.

Specifications

Best architectures adhere to SOLID Principles. Last thing any architect wants is code change in the repository whenever any developer need to query data with different predicates. That’s where this specification part of Core project comes to help.

using Library.Core.Entities;namespace Library.Core.Specifications.Books
{
public class BookWithAuthorAndCategorySpecification : BaseSpecification<Book>
{
public BookWithAuthorAndCategorySpecification() : base(){AddInclude(b => b.Author);AddInclude(b => b.Category);}}}

We can have multiple specifications in accordance to required queries. Just against any entity we’ll have a new specification class and call repository Get method with that class in the Service.

public async Task<IEnumerable<Book>> GetAllBooksAsync(){var spec = new BookWithAuthorAndCategorySpecification();return await repository.ListAsync(spec);}

Attach data layers to webAPI

  • Library.WebApi project
  • In ConfigureServices method of Startup.cs add following lines
// Add DbContextservices.AddDbContext<LibraryDbContext>(cfg =>{cfg.UseSqlServer(Configuration.GetConnectionString("LibraryConnectionString"));});// Add Repositoryservices.AddScoped<IGenericReadRepository, GenericReadRepository>();// Add Books Serviceservices.AddScoped<IBookService, BookService>();

This will make BookService injectable in the BooksController.

Automapper

In Library.WebApi project add following package.

<PackageReference Include="AutoMapper" Version="9.0.0" />

Automapper is a widely used utility, which frees developers from painstaking repetition of code for mapping ViewModels to Models (and other way round). Moreover, we can test our mappings. This is a very useful tutorial on that.

Profile

Have a folder named Mappings and CreateMap, i-e what member of ViewModel must be mapped to Model.

Dependency Injection

Add following in Startup.cs to make Automapper injectable.

//Add AutoMapper 
services.AddAutoMapper(typeof(Startup));

Map

In BooksController Map overload will use left side of type as target and parameter as source.

var books = mapper.Map<IEnumerable<BookResource>>(await service.GetAllBooksAsync());

BooksController

Now BooksController code in webAPI will look like that

using AutoMapper;using Library.Core.Interfaces;using Library.WebApi.Resources;using Microsoft.AspNetCore.Authorization;using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Logging;using System;using System.Collections.Generic;using System.Threading.Tasks;namespace Library.WebApi.Controllers{[Authorize]public class BooksController : BaseApiController{private readonly ILogger<BooksController> logger;private readonly IMapper mapper;private readonly IBookService service;public BooksController(ILogger<BooksController> logger,IMapper mapper,IBookService service){this.logger = logger;this.mapper = mapper;this.service = service;}[HttpGet][ProducesResponseType(typeof(BookResultResource), 200)]public async Task<IActionResult> Get(){try{logger.LogInformation("In BooksController Get");var bookResource = new BookResultResource(){UserFullName = GetUserInfo("name"),UserName = GetUserInfo("preferred_username"),Books =mapper.Map<IEnumerable<BookResource>>(await service.GetAllBooksAsync())};return Ok(bookResource);}catch (Exception ex){logger.LogError($"Error in BooksController: {ex.Message}");return BadRequest($"{BadRequest().StatusCode} : {ex.Message}");}}}}

Library.Web.UI

We won’t need any change in this project, just build and run as usual like previous post. And this time by clicking “Fetch Books” data will be served from database through Library.WebApi BooksController.

Sample

Clone following repository for code sample

Please don’t forget to add Application IDs for your UI and WebApi projects in authprovider.js and appsettings.json respectively.

https://github.com/AhsanRazaUK/webapi-ef-core

Summary

So, what we learnt in this article

  • Create and seed database using Entity Framework Core Code-First
  • Create and use generic repository in service
  • Create queries (specifications)
  • Inject service to webAPI
  • Automapper

And as usual, feel free to contact me if this all went well for you? Happy coding!


Originally published at http://ahsanshares.wordpress.com on November 4, 2019.

Ahsan Raza

Written by

Microsoft Certified .NET Developer, Team Leader and Technical Architect. Passionate about technology and culture. Love to learn and love to share.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade