Entity Framework Core Basic Querying

This document covers the fundamental ways to query data using Entity Framework Core (EF Core). EF Core provides a powerful and intuitive API for retrieving data from your database.

Introduction to Querying

EF Core's querying capabilities are built around the concept of LINQ (Language Integrated Query). LINQ allows you to write queries in C# or Visual Basic that are translated into SQL by EF Core.

The DbSet<TEntity> Property

Your EF Core DbContext class typically exposes properties of type DbSet<TEntity>. Each DbSet represents a collection of entities of a specific type in your database and serves as the entry point for querying.


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

    // ... other DbContext configurations
}
            

Basic Query Operations

Retrieving All Entities

To retrieve all entities of a given type, you can simply access the DbSet.


using (var context = new BloggingContext())
{
    var allBlogs = context.Blogs.ToList(); // Executes a query to fetch all blogs
}
            

The ToList() method executes the query and materializes the results into a List<Blog>.

Filtering Data (Where)

The Where extension method is used to filter results based on a predicate.


using (var context = new BloggingContext())
{
    var techBlogs = context.Blogs
                           .Where(b => b.Name.Contains("Technology"))
                           .ToList();
}
            

Selecting Specific Properties (Select)

The Select method allows you to project the results into a new shape, often selecting only specific properties.


using (var context = new BloggingContext())
{
    var blogNames = context.Blogs
                           .Select(b => b.Name)
                           .ToList(); // Returns a List<string>
}
            

Ordering Results (OrderBy and OrderByDescending)

You can sort the results using OrderBy and OrderByDescending.


using (var context = new BloggingContext())
{
    var sortedBlogs = context.Blogs
                             .OrderBy(b => b.Rating)
                             .ToList();

    var reverseSortedBlogs = context.Blogs
                                    .OrderByDescending(b => b.CreationDate)
                                    .ToList();
}
            

Taking a Specific Number of Results (Take)

The Take method limits the number of results returned.


using (var context = new BloggingContext())
{
    var firstFiveBlogs = context.Blogs
                                .OrderBy(b => b.Rating)
                                .Take(5)
                                .ToList();
}
            

Skipping Results (Skip)

The Skip method allows you to skip a specified number of results, often used for pagination.


using (var context = new BloggingContext())
{
    int pageSize = 10;
    int pageNumber = 2;
    var pageOfBlogs = context.Blogs
                             .OrderBy(b => b.Name)
                             .Skip((pageNumber - 1) * pageSize)
                             .Take(pageSize)
                             .ToList();
}
            

Counting Results (Count)

The Count() method can be used to get the number of entities that satisfy a condition.


using (var context = new BloggingContext())
{
    int numberOfTechBlogs = context.Blogs
                                   .Count(b => b.Name.Contains("Technology"));
}
            

Query Execution

EF Core uses lazy loading by default. This means that database queries are generally only executed when the data is actually needed (e.g., when you iterate over a collection or call a method like ToList()).

Note on Query Execution

Understanding when queries are executed is crucial for performance. Excessive calls to ToList() can lead to fetching more data than necessary.

Including Related Data (Navigation Properties)

EF Core allows you to easily load related data through navigation properties using the Include method. This avoids the need for separate queries to load related entities.

Example: Including Posts for each Blog


using (var context = new BloggingContext())
{
    var blogsWithPosts = context.Blogs
                                .Include(b => b.Posts) // Load related Posts
                                .ToList();

    foreach (var blog in blogsWithPosts)
    {
        Console.WriteLine($"Blog: {blog.Name}");
        foreach (var post in blog.Posts)
        {
            Console.WriteLine($"- Post: {post.Title}");
        }
    }
}
            

In this example, the query will fetch blogs and their associated posts in a single database roundtrip. If your models have complex relationships, you can chain Include calls.

LINQ to Entities vs. LINQ to Objects

It's important to distinguish between LINQ to Entities (queries executed against the database) and LINQ to Objects (queries executed against in-memory collections).

Tip

Always aim to perform filtering, sorting, and projection operations on the database side (using LINQ to Entities) whenever possible. This is generally more efficient than fetching all data into memory and then processing it.

Next Steps

Continue to the Advanced Querying section to explore more complex query scenarios, including filtering on related data, projections, and the use of the AsNoTracking() method for performance optimization.