ASP.NET Core 8 Web API with Entity Framework Core

Entity Framework Core (EF Core) is a powerful ORM (Object-Relational Mapper) for .NET, enabling developers to work with a database using .NET objects. This guide will cover how to integrate EF Core with ASP.NET Core 8 Web API, including setup, configuration, CRUD operations, advanced scenarios, and best practices.


1. Introduction to EF Core

Entity Framework Core (EF Core) is a lightweight, extensible, and cross-platform version of the popular Entity Framework data access technology. EF Core allows you to work with a database using .NET objects, simplifying data access code by allowing you to focus on the business logic rather than database interactions.


2. Setting Up EF Core in ASP.NET Core 8 Web API

2.1 Install EF Core NuGet Packages

Install the necessary EF Core packages using the .NET CLI:

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

        
    
2.2 Configure the DbContext

Create a DbContext class that represents a session with the database. It includes DbSet properties for each entity.

        
            
public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }

    public DbSet<Product> Products { get; set; }
}

        
    
2.3 Register DbContext in Dependency Injection

Register the DbContext in the DI container in Program.cs.

        
            
var builder = WebApplication.CreateBuilder(args);

// Add services to the container
builder.Services.AddControllers();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); //ms sql server 

var app = builder.Build();

app.MapControllers();
app.Run();

        
    

Add the connection string to appsettings.json.

        
            
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

        
    

3. Creating the Model

Create entity classes that represent the tables in the database.

        
            
public class Product
{
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    public decimal Price { get; set; }
}

        
    

4. Creating the Database

Use migrations to create and update the database schema based on your model classes.

4.1 Add Initial Migration

Add an initial migration to create the database schema.

        
            
dotnet ef migrations add InitialCreate

        
    
4.2 Apply the Migration

Apply the migration to create the database.

        
            
dotnet ef database update

        
    

5. Implementing CRUD Operations

5.1 Create a Repository Interface

Define a repository interface for data access.

        
            
public interface IProductRepository
{
    Task<IEnumerable<Product>> GetAllAsync();
    Task<Product> GetByIdAsync(int id);
    Task AddAsync(Product product);
    Task UpdateAsync(Product product);
    Task DeleteAsync(int id);
}

        
    
5.2 Implement the Repository

Implement the repository interface.

        
            
public class ProductRepository : IProductRepository
{
    private readonly ApplicationDbContext _context;

    public ProductRepository(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<IEnumerable<Product>> GetAllAsync()
    {
        return await _context.Products.ToListAsync();
    }

    public async Task<Product> GetByIdAsync(int id)
    {
        return await _context.Products.FindAsync(id);
    }

    public async Task AddAsync(Product product)
    {
        _context.Products.Add(product);
        await _context.SaveChangesAsync();
    }

    public async Task UpdateAsync(Product product)
    {
        _context.Products.Update(product);
        await _context.SaveChangesAsync();
    }

    public async Task DeleteAsync(int id)
    {
        var product = await _context.Products.FindAsync(id);
        if (product != null)
        {
            _context.Products.Remove(product);
            await _context.SaveChangesAsync();
        }
    }
}

        
    
5.3 Register the Repository

Register the repository in the DI container.

        
            
builder.Services.AddScoped<IProductRepository, ProductRepository>();

        
    
5.4 Implement the Controller

Create a controller that uses the repository to perform CRUD operations.

        
            
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductRepository _productRepository;

    public ProductsController(IProductRepository productRepository)
    {
        _productRepository = productRepository;
    }

    [HttpGet]
    public async Task<ActionResult<IEnumerable<Product>>> GetProducts()
    {
        var products = await _productRepository.GetAllAsync();
        return Ok(products);
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<Product>> GetProduct(int id)
    {
        var product = await _productRepository.GetByIdAsync(id);
        if (product == null)
        {
            return NotFound();
        }
        return Ok(product);
    }

    [HttpPost]
    public async Task<ActionResult<Product>> CreateProduct(Product product)
    {
        await _productRepository.AddAsync(product);
        return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product);
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateProduct(int id, Product product)
    {
        if (id != product.Id)
        {
            return BadRequest();
        }
        await _productRepository.UpdateAsync(product);
        return NoContent();
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteProduct(int id)
    {
        await _productRepository.DeleteAsync(id);
        return NoContent();
    }
}

        
    

6. Advanced Scenarios

6.1 Including Related Data

To include related data, use the Include method in your queries.

        
            
public class ProductRepository : IProductRepository
{
    private readonly ApplicationDbContext _context;

    public ProductRepository(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<IEnumerable<Product>> GetAllAsync()
    {
        return await _context.Products.Include(p => p.Category).ToListAsync();
    }
}

        
    
6.2 Handling Concurrency

To handle concurrency, use a concurrency token like a RowVersion property in your entities.

        
            
public class Product
{
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    public decimal Price { get; set; }
    [Timestamp]
    public byte[] RowVersion { get; set; }
}

        
    
6.3 Raw SQL Queries

You can execute raw SQL queries using the FromSqlRaw method.

        
            public async Task<IEnumerable<Product>> GetProductsByCategoryAsync(string category)
{
    return await _context.Products.FromSqlRaw(""SELECT * FROM Products WHERE Category = {0}"", category).ToListAsync();
}
        
    

7. Best Practices


8. Conclusion

Entity Framework Core is a powerful tool for managing data in ASP.NET Core 8 Web API applications. By understanding how to set up and configure EF Core, create and manage models, implement CRUD operations, and handle advanced scenarios, you can build robust and maintainable web applications. This comprehensive guide provides a solid foundation for mastering EF Core in ASP.NET Core 8 Web API.