Microsoft Developer Network

Entity Framework: Data Access Tutorial

Mastering Data Interaction with .NET

Introduction to Entity Framework

Entity Framework (EF) is a popular object-relational mapper (ORM) for .NET that enables developers to work with relational data using domain-specific objects. This tutorial will guide you through the fundamental concepts and practical applications of EF for data access in your .NET applications.

EF simplifies data access by abstracting away much of the boilerplate code typically required for database interactions. It allows you to query and manipulate data using C# or VB.NET code, treating your database tables as collections of objects.

Setting Up Your Development Environment

Before we begin, ensure you have the following prerequisites:

Creating a New Project

Start by creating a new .NET Core Console Application in Visual Studio. Name your project something descriptive, like EFCoreDataAccessDemo.

Installing Entity Framework Core Packages

You can install the necessary Entity Framework Core NuGet packages using the Package Manager Console or the NuGet Package Manager UI. We'll primarily use the following packages:

In the Package Manager Console, run:

Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools

Core Concepts of Entity Framework

Understanding these core concepts is crucial for effective use of Entity Framework:

1. DbContext

The DbContext class is the primary gateway to your database. It represents a session with the database and allows you to query and save data. You'll typically create a class that inherits from DbContext and represents your database context.

Example DbContext:

using Microsoft.EntityFrameworkCore;

public class ApplicationDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }

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

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Seed initial data if needed
        modelBuilder.Entity<Category>().HasData(
            new Category { Id = 1, Name = "Electronics" },
            new Category { Id = 2, Name = "Books" }
        );
    }
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public int CategoryId { get; set; }
    public Category Category { get; set; }
}

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Product> Products { get; set; }
}

2. DbSet

A DbSet<TEntity> represents a collection of all entities in the store (e.g., all instances of a particular entity type in the database) and can be used to query and save data for that entity type.

3. Migrations

EF Migrations is a powerful feature that allows you to evolve your database schema over time as your application models change. You can use it to create, update, and manage your database schema.

To enable migrations in your project, open the Package Manager Console and run:

Enable-Migrations

Then, create your first migration:

Add-Migration InitialCreate

And apply it to your database:

Update-Database

4. Model Configuration

You can configure your entity models using data annotations or the Fluent API within the OnModelCreating method of your DbContext. The Fluent API offers more control over complex mappings.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>()
        .Property(p => p.Price)
        .HasColumnType("decimal(18, 2)");

    modelBuilder.Entity<Product>()
        .HasOne(p => p.Category)
        .WithMany(c => c.Products)
        .HasForeignKey(p => p.CategoryId);
}

Performing CRUD Operations

Entity Framework provides intuitive ways to perform standard Create, Read, Update, and Delete operations.

Creating (Adding) New Data

To add a new entity to the database, create an instance of your entity, attach it to the DbContext, and then call SaveChanges.

using var context = new ApplicationDbContext(...); // Assume context is configured

var newProduct = new Product
{
    Name = "Laptop",
    Price = 1200.50m,
    CategoryId = 1 // Assuming CategoryId for Electronics
};

context.Products.Add(newProduct);
await context.SaveChangesAsync();

Reading (Querying) Data

You can query data using LINQ (Language Integrated Query). EF translates your LINQ queries into SQL.

using var context = new ApplicationDbContext(...);

// Get all products
var allProducts = await context.Products.ToListAsync();

// Get a specific product by ID
var product = await context.Products.FindAsync(1);

// Query with filtering and ordering
var expensiveElectronics = await context.Products
    .Where(p => p.Category.Name == "Electronics" && p.Price > 1000)
    .OrderBy(p => p.Name)
    .ToListAsync();

// Using Include to load related data
var productsWithCategories = await context.Products
    .Include(p => p.Category)
    .ToListAsync();

Updating Data

To update an existing entity, retrieve it from the context, modify its properties, and then call SaveChanges.

using var context = new ApplicationDbContext(...);

var productToUpdate = await context.Products.FindAsync(1);
if (productToUpdate != null)
{
    productToUpdate.Price = 1250.00m;
    await context.SaveChangesAsync();
}

Deleting Data

To delete an entity, retrieve it, mark it for deletion using Remove, and then call SaveChanges.

using var context = new ApplicationDbContext(...);

var productToDelete = await context.Products.FindAsync(2);
if (productToDelete != null)
{
    context.Products.Remove(productToDelete);
    await context.SaveChangesAsync();
}

Advanced Topics

Once you're comfortable with the basics, explore these advanced features:

1. Change Tracking

EF automatically tracks changes to entities. You can inspect the state of entities and understand how changes are managed.

2. Lazy Loading vs. Eager Loading

Understand the differences between lazy loading (loading related data on demand) and eager loading (loading related data immediately using Include) and when to use each.

3. Stored Procedures and Raw SQL

While EF promotes LINQ-based queries, you can also execute stored procedures or raw SQL queries when necessary.

var productsFromSql = await context.Products
    .FromSqlRaw("SELECT * FROM Products WHERE Price > {0}", 500)
    .ToListAsync();

4. Asynchronous Operations

Always use asynchronous methods like ToListAsync(), FindAsync(), and SaveChangesAsync() in asynchronous code to prevent blocking threads.

5. Performance Optimization

Learn techniques for optimizing EF performance, such as:

Conclusion

Entity Framework provides a robust and flexible way to interact with your database in .NET applications. By understanding its core concepts and features, you can significantly improve your productivity and the maintainability of your data access code.

This tutorial covered the essentials, from setting up your environment and understanding the DbContext to performing basic CRUD operations and touching upon advanced topics. Continue exploring the official Microsoft documentation for more in-depth information and advanced patterns.

Next Steps: Explore building web APIs with Entity Framework Core, learn about data validation, and investigate strategies for handling concurrency.