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 *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 *Dependency Injection and fulfils SOLID principles.

Introduction

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.

  • In *Library.WebApi *project, add reference of *Library.Infrastructure *project

  • Open *Package Manager Console, *select *Library.Infrastructure *as *Default Project and *

PM> Add-Migration initial

  • We’ll get *Migrations *folder automatically and we’ll get a class created for us. We may edit it in accordance to our need.

  • Back in Package Manager Console use command to update database using this migration
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 hold data access code. In this application we’ll see the magic of generic repositories, i-e repository that’ll take entity as a parameter and perform data access operations on it.

I know there’s in favour and against discussion in developers circles about repository pattern and sepcifically do we really need it 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 need to change in classes (specifically core classes) whenever we want to query data with different predicate. 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);
        }
    }
}

Now when we can have multiple *specifications *without making changes in our repository. 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 DbContext 
            services.AddDbContext<LibraryDbContext>(cfg =>
            {
                cfg.UseSqlServer(
                    Configuration
                    .GetConnectionString("LibraryConnectionString"));
            });

            // Add Repository
            services.AddScoped<IGenericReadRepository, GenericReadRepository>();

            // Add Books Service
            services.AddScoped<IBookService, BookService>();

This will make BookService injectable in controller

Automapper

In *Library.WebApi *project add following package.

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

Automapper is a widely used utility, which frees coders from mapping ViewModels to *Models *in controllers. This is a very useful tutorial in case you are new to that.
Automapper also requires to be added in *ConfigureServices *method

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

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!

How to make Web API with ASP.NET Core 3.0 and MongoDB

#aspnet #asp.net #aspnetcore

How to Create WebAPI with ASP.NET Core 3.0 and Entity Framework Core
1 Likes110.25 GEEK