Data Access with Entity Framework Core

Introduction to EF Core

Entity Framework Core (EF Core) is a modern, lightweight, cross-platform, and extensible object-relational mapper (ORM) for .NET. It enables .NET developers to work with a database using domain-specific objects that are essentially ADO.NET entity types. EF Core is a complete rewrite of the popular Entity Framework 6, with significant architectural improvements and performance enhancements.

EF Core provides a convenient way to:

  • Map .NET objects to database tables.
  • Persist these objects to the database.
  • Query data from the database using LINQ (Language Integrated Query).
  • Manage database schema changes using migrations.

Installation

You can install EF Core and its providers via NuGet packages. The most common provider is for SQL Server.

Tip: Ensure you have the .NET SDK installed. You can manage packages through Visual Studio's NuGet Package Manager or using the `dotnet add package` command-line tool.

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

Microsoft.EntityFrameworkCore.Tools is essential for working with migrations from the command line.

Creating a Model

EF Core uses Plain Old CLR Objects (POCOs) to represent your database schema. These are simple classes with properties that map to columns in your database tables.


public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
    public string Rating { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}
                

EF Core follows certain conventions for discovering entity types and their relationships. For example, properties named `Id` or `[EntityName]Id` are typically recognized as primary keys.

Setting up the DbContext

The DbContext class is the entry point for interacting with your EF Core model and database. It represents a session around a database, allowing you to query and save data.


using Microsoft.EntityFrameworkCore;

public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

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

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Configure relationships or other model details here
        modelBuilder.Entity<Blog>()
            .Property(b => b.Url)
            .IsRequired();
    }

    // Optional: Configure the database connection string directly if not using DI
    // protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    // {
    //     optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=BloggingEFCore;Trusted_Connection=True;");
    // }
}
                

In a modern ASP.NET Core application, you'll typically configure the DbContext using Dependency Injection (DI) in your Startup.cs or Program.cs file.

Migrations

Migrations allow you to evolve your database schema over time as your application model changes. EF Core Migrations can:

  • Create new tables, columns, and constraints based on your model.
  • Apply changes to an existing database.
  • Generate SQL scripts for manual deployment.

To add a new migration:


dotnet ef migrations add InitialCreate
                

This command will generate a new folder named Migrations containing code that describes the changes to be applied to your database. To apply the migration to your database:


dotnet ef database update
                
Important: Ensure you have installed the Microsoft.EntityFrameworkCore.Tools NuGet package and that your startup project is correctly configured for the `dotnet ef` commands to find your `DbContext`.

Querying Data

You can use LINQ to query data from your database through your DbContext.


using (var context = new BloggingContext(options)) // Assuming options are configured
{
    // Get all blogs
    var blogs = context.Blogs.ToList();

    // Get blogs with a specific URL
    var specificBlog = context.Blogs.FirstOrDefault(b => b.Url.Contains("microsoft"));

    // Include related data (e.g., posts for each blog)
    var blogsWithPosts = context.Blogs.Include(b => b.Posts).ToList();

    // Filter and order posts
    var recentPosts = context.Posts
                            .Where(p => p.Blog.Url == "example.com")
                            .OrderByDescending(p => p.PostId)
                            .ToList();
}
                

EF Core translates these LINQ queries into SQL statements that are executed against the database.

Saving Data

Adding, updating, and deleting entities involves modifying the state of entities tracked by the DbContext and then calling SaveChanges().


using (var context = new BloggingContext(options))
{
    // Adding a new entity
    var newBlog = new Blog { Url = "newblog.com", Rating = "Good" };
    context.Blogs.Add(newBlog);
    context.SaveChanges(); // Persists the new blog to the database

    // Updating an existing entity
    var blogToUpdate = context.Blogs.Find(1); // Or use FirstOrDefault
    if (blogToUpdate != null)
    {
        blogToUpdate.Rating = "Excellent";
        context.SaveChanges(); // Updates the blog in the database
    }

    // Deleting an entity
    var blogToDelete = context.Blogs.FirstOrDefault(b => b.Url == "obsolete.com");
    if (blogToDelete != null)
    {
        context.Blogs.Remove(blogToDelete);
        context.SaveChanges(); // Deletes the blog from the database
    }
}
                
Note: EF Core tracks changes to entities that are retrieved from the database. When you call SaveChanges(), it generates and executes the necessary SQL commands to persist those changes.

Relationships

EF Core supports various relationships, including one-to-one, one-to-many, and many-to-many.

One-to-Many (Blog to Posts)

As shown in the model definition, a Blog can have many Posts, and each Post belongs to one Blog. The foreign key property BlogId in the Post class and the navigation property Blog in Post, and the collection Posts in Blog define this relationship.

Configuring Relationships

You can configure relationships explicitly in OnModelCreating if EF Core's conventions don't match your needs.


protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>()
        .HasOne(p => p.Blog)
        .WithMany(b => b.Posts)
        .HasForeignKey(p => p.BlogId);
}
                

This explicitly defines the one-to-many relationship between Post and Blog.