Ora

How to use entity framework in .NET Core web api?

Published in .NET Core Web API EF 7 mins read

Integrating Entity Framework (EF) Core into a .NET Core Web API provides a powerful and efficient way to interact with your database using an object-relational mapper (ORM). It allows developers to work with database operations using .NET objects, abstracting away much of the complexities of raw SQL queries.

Understanding Entity Framework Core in Web API

Entity Framework Core simplifies data access by mapping database tables to C# classes (models) and allowing developers to perform CRUD (Create, Read, Update, Delete) operations directly on these objects. This approach significantly speeds up development and improves maintainability.

Key EF Core Concepts

Before diving into implementation, it's essential to grasp these core concepts:

  • Models (Entities): Plain Old C# Objects (POCOs) that represent tables in your database. Each property in the class typically maps to a column in the table.
  • DbContext: The primary class responsible for interacting with the database. It manages entity instances, tracks changes, and persists data. It acts as a bridge between your application's entities and the database.
  • DbSet: A property on the DbContext that represents a collection of a specific entity type (e.g., DbSet<Product>). It allows you to query and save instances of that entity.
  • Migrations: A feature that allows you to evolve your database schema as your model classes change. It generates code that can be applied to update your database.

Step-by-Step Guide to Using EF Core in .NET Core Web API

Here's a comprehensive guide to setting up and using Entity Framework Core in your .NET Core Web API.

1. Create a New .NET Core Web API Project

Start by creating a new Web API project in Visual Studio or via the .NET CLI.

dotnet new webapi -n MyWebApiProject
cd MyWebApiProject

2. Install Necessary NuGet Packages

You'll need to install EF Core packages for your chosen database provider (e.g., SQL Server, SQLite, PostgreSQL).

Package Name Description
Microsoft.EntityFrameworkCore.SqlServer For connecting to SQL Server databases.
Microsoft.EntityFrameworkCore.Tools Provides PowerShell commands for migrations (e.g., Add-Migration, Update-Database).
Microsoft.EntityFrameworkCore.Design Required for design-time operations, especially when using Microsoft.EntityFrameworkCore.Tools.

You can install them using the .NET CLI:

dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.EntityFrameworkCore.Design

3. Define Your Entity Models

Create C# classes that represent your database tables. For example, let's create a Product model:

// Models/Product.cs
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public int StockQuantity { get; set; }
}

4. Create Your DbContext Class

Create a class that inherits from Microsoft.EntityFrameworkCore.DbContext. This class will manage your database sessions and entity sets.

// Data/InventoryContext.cs
using Microsoft.EntityFrameworkCore;

namespace MyWebApiProject.Data
{
    public class InventoryContext : DbContext
    {
        public InventoryContext(DbContextOptions<InventoryContext> options) : base(options)
        {
        }

        public DbSet<Product> Products { get; set; }
        // Add other DbSet properties for additional entities
    }
}

5. Configure the Database Connection

Specify your database connection string in appsettings.json and register your DbContext with the dependency injection container in Program.cs.

a. appsettings.json

Add your connection string. For SQL Server:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyWebApiDb;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

b. Program.cs

Register InventoryContext using dependency injection:

using Microsoft.EntityFrameworkCore;
using MyWebApiProject.Data; // Assuming your DbContext is in Data folder

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// Configure Entity Framework Core with SQL Server
builder.Services.AddDbContext<InventoryContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

var app = builder.Build();

// ... rest of your Program.cs code

6. Create and Apply Database Migrations

Migrations allow you to manage your database schema.

a. Add a Migration

Open your Package Manager Console (in Visual Studio) or command prompt (in project root) and run:

dotnet ef migrations add InitialCreate

This command generates a new folder named Migrations containing files that define how to create your database schema based on your DbContext and model classes.

b. Update the Database

Apply the generated migration to your database:

dotnet ef database update

This command creates the database (if it doesn't exist) and the Products table.

7. Create API Controllers with EF Core

Now you can create API controllers that interact with your database using the InventoryContext.

Option A: Scaffolding API Controller (Visual Studio)

Visual Studio provides a convenient way to scaffold a controller with full CRUD operations using Entity Framework:

  1. Right-click the Controllers folder in your project, choose Add, and then click Controller.
  2. Select the "API Controller with actions using Entity Framework" template.
  3. In the next dialog:
    • Choose your model class (e.g., Product).
    • Choose your context class (e.g., InventoryContext).
    • Name the control (e.g., ProductsController).
  4. Click Add.

This will generate a ProductsController.cs file with methods for GET, POST, PUT, and DELETE operations, all utilizing your InventoryContext.

Option B: Manual Controller Implementation

If you prefer to write the controller manually or are using a different IDE, you'll inject your DbContext into the controller's constructor and use it to perform database operations.

// Controllers/ProductsController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MyWebApiProject.Data;
using MyWebApiProject.Models;

namespace MyWebApiProject.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase
    {
        private readonly InventoryContext _context;

        public ProductsController(InventoryContext context)
        {
            _context = context;
        }

        // GET: api/Products
        [HttpGet]
        public async Task<ActionResult<IEnumerable<Product>>> GetProducts()
        {
            return await _context.Products.ToListAsync();
        }

        // GET: api/Products/5
        [HttpGet("{id}")]
        public async Task<ActionResult<Product>> GetProduct(int id)
        {
            var product = await _context.Products.FindAsync(id);

            if (product == null)
            {
                return NotFound();
            }

            return product;
        }

        // POST: api/Products
        [HttpPost]
        public async Task<ActionResult<Product>> PostProduct(Product product)
        {
            _context.Products.Add(product);
            await _context.SaveChangesAsync();

            return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product);
        }

        // PUT: api/Products/5
        [HttpPut("{id}")]
        public async Task<IActionResult> PutProduct(int id, Product product)
        {
            if (id != product.Id)
            {
                return BadRequest();
            }

            _context.Entry(product).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!ProductExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }

        // DELETE: api/Products/5
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteProduct(int id)
        {
            var product = await _context.Products.FindAsync(id);
            if (product == null)
            {
                return NotFound();
            }

            _context.Products.Remove(product);
            await _context.SaveChangesAsync();

            return NoContent();
        }

        private bool ProductExists(int id)
        {
            return _context.Products.Any(e => e.Id == id);
        }
    }
}

8. Run Your API

Build and run your .NET Core Web API. You can then use tools like Postman, Swagger UI (if configured), or a web browser to test your API endpoints and interact with the database.

  • GET /api/products: Retrieve all products.
  • GET /api/products/1: Retrieve product with ID 1.
  • POST /api/products: Create a new product.
  • PUT /api/products/1: Update product with ID 1.
  • DELETE /api/products/1: Delete product with ID 1.

Best Practices and Further Considerations

  • Asynchronous Operations: Always use async and await with EF Core methods like ToListAsync(), FindAsync(), and SaveChangesAsync() to keep your API responsive.
  • Data Transfer Objects (DTOs): For production applications, it's often recommended to use DTOs instead of directly exposing your entity models in API responses. DTOs allow you to control the data shape, hide sensitive information, and prevent over-posting attacks.
  • Error Handling: Implement robust error handling, including specific exception handling for database-related issues.
  • Validation: Use data annotations or Fluent Validation for input validation on your models.
  • Logging: Configure logging to monitor database interactions and identify performance bottlenecks or errors.
  • Repository Pattern & Unit of Work: For larger applications, consider implementing a repository pattern and unit of work to abstract data access logic and improve testability.
  • Eager/Lazy Loading: Understand how EF Core loads related data (eager loading with Include(), lazy loading if configured).

By following these steps, you can effectively use Entity Framework Core to build robust and scalable data-driven .NET Core Web APIs.