Implement Entity Framework A Code First Approach in .Net 8 API

.NET 8 CRUD Operations

It's a continuation of part 1.

Introduction

In this article, we are going to discuss What Entity Framework is and How we can implement it in the .Net 8 project. This is a continuation of part 1, so if you are new to this article, please check my part 1 before proceeding. In this article, we are going to implement EF8 code first approach.

What is Entity Framework?

  • Entity Framework (EF) is an object-relational mapper that enables .NET developers to work with relational data using domain-specific objects.
  • It eliminates the need for most of the data-access code that developers usually need to write.
  • Its purpose is to abstract the ties to a relational database.

Why Entity Framework?

Entity Framework is an ORM, and ORMs are aimed to increase the developer’s productivity by reducing the redundant task of persisting the data used in our applications.

Features of Entity Framework

  • Entity Framework is a lightweight and extensible object-relational mapping (ORM) technology.
  • Entity Framework supports multiple platforms like Windows, Linux, and macOS.
  • Entity Framework supports both relational and non-relational data sources.
  • Entity Framework works efficiently with widely used databases like SQL Server, SQL Server Compact, SQLite, and PostgreSQL.
  • Entity Framework makes it easier for programmers to perform create, read, update, and delete (CRUD) operations by supporting databases. It also makes it easier for developers to perform unit testing by keeping in-memory tables.

Entity Framework Development Approaches

There are three approaches to creating entity frameworks.

Code First Approach

The Code First approach enables us to create a model and relation using classes and then create the database from these classes. It enables us to work with the Entity Framework in an object-oriented manner. In this approach, you can use empty databases and add tables too.

Model First Approach

In this approach, model classes and their relation are created first using the ORM designer, and the physical database will be generated using this model. The Model First approach means we create a diagram of the entity and relation that will be converted automatically into a code model.

Database First Approach

The Database First approach enables us to create an entity model from the existing database. This approach helps us to reduce the amount of code that we need to write. The following procedure will create an entity model using the Database First approach.

Prerequisites

  • Visual Studio 2022 (Any Edition – Community / Professional / Enterprise)
  • Microsoft SQL Server 2008 or above.
  • .Net Core 8 SDK or Later Version

Steps to Follow to Implement EF8

Step 1. Install Entity Framework Package from NuGet.

Right Click on your project -> Click on"Manage NuGet Packages" -> Open "Browse" tab -> search below EF8 package

  1. Microsoft.EntityFrameworkCore
  2. Microsoft.EntityFrameworkCore.Design
  3. Microsoft.EntityFrameworkCore.Tools
  4. Microsoft.EntityFrameworkCore.SqlServer

Install Entity Framework package from NuGet

Step 2. Add OurHeroDbContext file in our project.

  • Open a solution
  • Right-click and add the "Entity" folder
  • add OurHeroDbContext class (select "Entity" folder and press Ctrl +Shift +A to create a class )
  • inherit DbContext class
  • Add constructor and accept EF option and send to DbContext
// OurHeroDbContext.cs
using Microsoft.EntityFrameworkCore;

namespace DotNet8WebAPI.Entity
{
    public class OurHeroDbContext : DbContext
    {
        public OurHeroDbContext(DbContextOptions<OurHeroDbContext> options) : base(options)
        {
        }
    }
}

Step 3. Registered DB Model in OurHeroDbContext file.

Configured our EF model and loaded pre-defined data(master data).

// OurHeroDbContext.cs
using DotNet8WebAPI.Model;
using Microsoft.EntityFrameworkCore;

namespace DotNet8WebAPI.Entity
{
    public class OurHeroDbContext : DbContext
    {
        public OurHeroDbContext(DbContextOptions<OurHeroDbContext> options) : base(options)
        {
        }
        // Registered DB Model in OurHeroDbContext file 
        public DbSet<OurHero> OurHeros { get; set; }

        /*
         OnModelCreating mainly used to configured our EF model 
         And insert master data if required
        */
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // Setting a primary key in OurHero model
            modelBuilder.Entity<OurHero>().HasKey(x => x.Id);

            // Inserting record in OurHero table
            modelBuilder.Entity<OurHero>().HasData(
                new OurHero
                {
                    Id = 1,
                    FirstName = "System",
                    LastName = "",
                    isActive = true,
                }
            );
        }
    }
}

Step 4. Add ConnectionStrings in appsettings.json file

"ConnectionStrings": {
  "OurHeroConnectionString": "Data Source=LAPTOP-4TSM9SDC;Initial Catalog=OurHeroDB; Integrated Security=true;TrustServerCertificate=True;"
}
//appsettings.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
    "OurHeroConnectionString": "Data Source=LAPTOP-4TSM9SDC;Initial Catalog=OurHeroDB; Integrated Security=true;TrustServerCertificate=True;"
  },
  "AllowedHosts": "*"
}

Step 5. Register DbContext

Select your Database - I'm using an SQL server as a database. That's why I called the UseSqlServer method.

Provide ConnectionString

//*********************** Register DbContext and provide ConnectionString .***********************
builder.Services.AddDbContext<OurHeroDbContext>(db => db.UseSqlServer(builder.Configuration.GetConnectionString("OurHeroConnectionString")), ServiceLifetime.Singleton);
//*********************** Register DbContext end.***********************
// Program.cs

using DotNet8WebAPI.Entity;
using DotNet8WebAPI.Services;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

//*********************** Add services to the container.***********************
builder.Services.AddTransient<IOurHeroService, OurHeroService>();
//*********************** Add services to the container end.***********************

//*********************** Register DbContext and provide ConnectionString .***********************
builder.Services.AddDbContext<OurHeroDbContext>(db => db.UseSqlServer(builder.Configuration.GetConnectionString("OurHeroConnectionString")), ServiceLifetime.Singleton);
//*********************** Register DbContext end.***********************

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

Step 6. Go to OurHeroService and inject our OurHeroDbContext in the constructor.

// OurHeroService.cs

using DotNet8WebAPI.Entity;
using DotNet8WebAPI.Model;

namespace DotNet8WebAPI.Services
{
    public class OurHeroService : IOurHeroService
    {
        private readonly OurHeroDbContext _db;
        public OurHeroService(OurHeroDbContext db)
        {
            _db = db;
        }
    }
}

Step 7. Now use db context instead of in-memory collection to fetch OurHeros record.

Use async-await to fetch the OurHeros record asynchronously.

instead of returning pain object return as a Task<List<OurHero>>

List<OurHero> --> Task<List<OurHero>>

// OurHeroService.cs

using DotNet8WebAPI.Entity;
using DotNet8WebAPI.Model;
using Microsoft.EntityFrameworkCore;

namespace DotNet8WebAPI.Services
{
    public class OurHeroService : IOurHeroService
    {
        private readonly OurHeroDbContext _db;
        public OurHeroService(OurHeroDbContext db)
        {
            _db = db;
        }

        public async Task<List<OurHero>> GetAllHeros(bool? isActive)
        {
            if (isActive == null) { return await _db.OurHeros.ToListAsync(); }

            return await _db.OurHeros.Where(obj => obj.isActive == isActive).ToListAsync();
        }

        public async Task<OurHero?> GetHerosByID(int id)
        {
            return await _db.OurHeros.FirstOrDefaultAsync(hero => hero.Id == id);
        }

        public async Task<OurHero?> AddOurHero(AddUpdateOurHero obj)
        {
            var addHero = new OurHero()
            {
                FirstName = obj.FirstName,
                LastName = obj.LastName,
                isActive = obj.isActive,
            };

            _db.OurHeros.Add(addHero);
            var result = await _db.SaveChangesAsync();
            return result >= 0 ? addHero : null;
        }

        public async Task<OurHero?> UpdateOurHero(int id, AddUpdateOurHero obj)
        {
            var hero = await _db.OurHeros.FirstOrDefaultAsync(index => index.Id == id);
            if (hero != null)
            {
                hero.FirstName = obj.FirstName;
                hero.LastName = obj.LastName;
                hero.isActive = obj.isActive;

                var result = await _db.SaveChangesAsync();
                return result >= 0 ? hero : null;
            }
            return null;
        }

        public async Task<bool> DeleteHerosByID(int id)
        {
            var hero = await _db.OurHeros.FirstOrDefaultAsync(index => index.Id == id);
            if (hero != null)
            {
                _db.OurHeros.Remove(hero);
                var result = await _db.SaveChangesAsync();
                return result >= 0;
            }
            return false;
        }
    }
}

Step 8. In IOurHeroService.

Update method return type.

Return as a Task instead of the normal class model.

List<OurHero> GetAllHeros(bool? isActive);

Task<List<OurHero>> GetAllHeros(bool? isActive);

// IOurHeroService.cs
using DotNet8WebAPI.Model;

namespace DotNet8WebAPI.Services
{
    public interface IOurHeroService
    {
        Task<List<OurHero>> GetAllHeros(bool? isActive);
        Task<OurHero?> GetHerosByID(int id);
        Task<OurHero?> AddOurHero(AddUpdateOurHero obj);
        Task<OurHero?> UpdateOurHero(int id, AddUpdateOurHero obj);
        Task<bool> DeleteHerosByID(int id);
    }
}

Step 9. Open OurHeroController file.

Implement async-await in all action methods because now our IOurHeroService returns a response asynchronously.

[HttpGet]
public IActionResult Get([FromQuery] bool? isActive = null)
{
return Ok(_heroService.GetAllHeros(isActive));
}

[HttpGet]
public async Task<IActionResult> Get([FromQuery] bool? isActive = null)
{
var heros = await _heroService.GetAllHeros(isActive);
return Ok(heros);
}

// OurHeroController.cs
using DotNet8WebAPI.Model;
using DotNet8WebAPI.Services;
using Microsoft.AspNetCore.Mvc;

namespace DotNet8WebAPI.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class OurHeroController : ControllerBase
    {
        private readonly IOurHeroService _heroService;
        public OurHeroController(IOurHeroService heroService)
        {
            _heroService = heroService;
        }

        [HttpGet]
        public async Task<IActionResult> Get([FromQuery] bool? isActive = null)
        {
            var heros = await _heroService.GetAllHeros(isActive);
            return Ok(heros);
        }

        [HttpGet("{id}")]
        //[Route("{id}")] // /api/OurHero/:id
        public async Task<IActionResult> Get(int id)
        {
            var hero = await _heroService.GetHerosByID(id);
            if (hero == null)
            {
                return NotFound();
            }
            return Ok(hero);
        }

        [HttpPost]
        public async Task<IActionResult> Post([FromBody] AddUpdateOurHero heroObject)
        {
            var hero = await _heroService.AddOurHero(heroObject);

            if (hero == null)
            {
                return BadRequest();
            }

            return Ok(new
            {
                message = "Super Hero Created Successfully!!!",
                id = hero!.Id
            });
        }

        [HttpPut]
        [Route("{id}")]
        public async Task<IActionResult> Put([FromRoute] int id, [FromBody] AddUpdateOurHero heroObject)
        {
            var hero = await _heroService.UpdateOurHero(id, heroObject);
            if (hero == null)
            {
                return NotFound();
            }

            return Ok(new
            {
                message = "Super Hero Updated Successfully!!!",
                id = hero!.Id
            });
        }

        [HttpDelete]
        [Route("{id}")]
        public async Task<IActionResult> Delete([FromRoute] int id)
        {
            if (!await _heroService.DeleteHerosByID(id))
            {
                return NotFound();
            }

            return Ok(new
            {
                message = "Super Hero Deleted Successfully!!!",
                id = id
            });
        }
    }
}

Step 10. Now, our Entity Framework integration is almost ready.

Next

Visual Studio

  • Open the Tools menu (available in the Visual Studio toolbar)
  • Select NuGet package manager
  • Then Select Package Manager Console
    Package manager console
Package Manager Console Description
add-migration [name] Create a new migration with the specific migration name.
remove-migration Remove the latest migration.
update-database Update the database to the latest migration.
update-database [name] Update the database to a specific migration name point.
get-migrations Lists all available migrations.
script-migration Generates an SQL script for all migrations.
drop-database Drop the database.

Run the add-migration [name] command to generate a DB migration file.

Once the file is ready

DB Migration

Then, run the update-database command to reflect the migration change on our database side.

Update Database

After running the update-database command. If you are also getting the same Error.

Only the invariant culture is supported in the globalization-invariant mode.

Then follow the below stem to fix this issue.

  • Open a solution explorer
  • Double-click on your project
  • It will open the .csproj file
  • update the InvariantGlobalization setting from true to false (It's available in the PropertyGroup section)
  • Save .csproj file
  • Run the update-database command again

Once the update database runs successfully, you will verify your Database in the SQL Server like this.

Verify database

Step 11. Now, our Entity Framework has been implemented successfully.

We can run our application and verify.

Run Application

Currently, we have one entry because we are inserted using the OnModelCreating method.

Now, I'm going to insert one more entry using post-API (/api/OurHero)

Post API

Summary

That's it! You've created a complete .NET 8 Web API for CRUD operations with an SQL Server database. You can now integrate this API into your front-end application.

Comment your query if anything is not working for you.

Thanks for reading.

Previous Article