DbContext API

The DbContext class is the central hub for interacting with your database in Entity Framework Core. It represents a session with the database and allows you to query and save data. Understanding the DbContext API is fundamental to effectively using Entity Framework Core.

Key Concept: DbContext

A DbContext instance is a lightweight object that is typically created for a single unit of work. It manages the database connection, change tracking, and transaction management.

Core Properties

The DbContext class provides several essential properties for managing your data context:

Property Description
Database Provides access to the underlying database connection and operations. You can use it to execute raw SQL, manage transactions, and more.
ChangeTracker Exposes information about the entities currently being tracked by the context, including their states (Added, Modified, Deleted, Unchanged).
Model Represents the EF Core model for the context, providing access to entity types, properties, and relationships.

Core Methods

These methods are the primary way you interact with your data through the DbContext:

Method Description
Add<TEntity>(TEntity entity) Adds a new entity to the context. The entity will be inserted into the database when SaveChanges is called.
AddRange<TEntity>(IEnumerable<TEntity> entities) Adds multiple entities to the context.
Update<TEntity>(TEntity entity) Marks an existing entity as modified. All properties will be updated in the database when SaveChanges is called.
UpdateRange<TEntity>(IEnumerable<TEntity> entities) Marks multiple existing entities as modified.
Remove<TEntity>(TEntity entity) Marks an entity for deletion. The entity will be deleted from the database when SaveChanges is called.
RemoveRange<TEntity>(IEnumerable<TEntity> entities) Marks multiple entities for deletion.
SaveChanges() Persists all pending changes (adds, updates, deletes) to the database. Returns the number of state entries written to the database.
SaveChangesAsync(CancellationToken cancellationToken = default) Asynchronously persists all pending changes to the database.
Find<TEntity>(params object[] keyValues) Finds an entity with the given primary key value. It first checks in the context cache, then queries the database if not found.
Entry<TEntity>(TEntity entity) Gets the EntityEntry<TEntity> object for the given entity. This provides detailed information and control over the entity's state and properties.

Change Tracking

DbContext automatically tracks changes to entities. When you retrieve an entity, EF Core monitors it for modifications. The ChangeTracker property allows you to inspect and manipulate these tracked entities.

Entity States

Entities can be in one of the following states:

  • Added: The entity is new and will be inserted.
  • Modified: The entity has been changed and will be updated.
  • Deleted: The entity will be removed.
  • Unchanged: The entity has not been changed.
  • Detached: The entity is not being tracked by the context.

You can manually control the state of an entity using the Entry method:


var blog = _context.Blogs.Find(1);
if (blog != null)
{
    // Mark as modified explicitly
    _context.Entry(blog).State = EntityState.Modified;
    // Or, if you only modified specific properties:
    // _context.Entry(blog).Property(b => b.Url).IsModified = true;
}
            

Querying Data

Entities are exposed through DbSet<TEntity> properties on your DbContext. You use LINQ to query these sets.


// Get all blogs
var allBlogs = _context.Blogs.ToList();

// Get blogs with a specific URL
var specificBlog = _context.Blogs
    .Where(b => b.Url.Contains("example.com"))
    .FirstOrDefault();

// Include related data
var blogsWithPosts = _context.Blogs
    .Include(b => b.Posts) // Assuming a navigation property named Posts
    .ToList();
            

Executing Raw SQL

For scenarios where LINQ is not sufficient, you can execute raw SQL queries:


var blogsFromSql = _context.Blogs.FromSqlRaw("SELECT * FROM Blogs");
            

Saving Data

The SaveChanges method is crucial for persisting your changes. It performs batch operations and handles concurrency.


// Add a new blog
var newBlog = new Blog { Url = "http://newblog.com" };
_context.Blogs.Add(newBlog);
int rowsAffected = _context.SaveChanges(); // Commits the addition

// Update an existing blog
var existingBlog = _context.Blogs.Find(2);
if (existingBlog != null)
{
    existingBlog.Url = "http://updatedurl.com";
    _context.SaveChanges(); // Commits the update
}

// Delete a blog
var blogToDelete = _context.Blogs.Find(3);
if (blogToDelete != null)
{
    _context.Blogs.Remove(blogToDelete);
    _context.SaveChanges(); // Commits the deletion
}
            

Transactions

By default, SaveChanges runs within a database transaction. If any operation fails, the entire transaction is rolled back, ensuring data consistency.

Configuration

You configure your DbContext, typically in its constructor, by overriding the OnConfiguring method or by using dependency injection with ConfigureServices in ASP.NET Core.


// Example using OnConfiguring
public class MyDbContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True;");
    }

    // ... other DbContext methods
}
            

Dependency Injection

In modern applications, especially web applications, DbContext is typically registered with a dependency injection container and injected into your services rather than configured directly in OnConfiguring.