Query Types


This feature is new in EF Core 2.1

Query Types are read-only query result types that can be added to the EF Core model. Query Types enable ad-hoc querying (like anonymous types), but are more flexible because they can have mapping configuration specified.

They are conceptually similar to Entity Types in that:

  • They are POCO C# types that are added to the model, either in OnModelCreating using the ModelBuilder.Query method, or via a DbContext "set" property (for query types such a property is typed as DbQuery<T> rather than DbSet<T>).
  • They support much of the same mapping capabilities as regular entity types. For example, inheritance mapping, navigations (see limitiations below) and, on relational stores, the ability to configure the target database schema objects via ToTable, HasColumn fluent-api methods (or data annotations).

Query Types are different from entity types in that they:

  • Do not require a key to be defined.
  • Are never tracked by the Change Tracker.
  • Are never discovered by convention.
  • Only support a subset of navigation mapping capabilities - Specifically, they may never act as the principal end of a relationship.
  • May be mapped to a defining query - A Defining Query is a secondary query that acts a data source for a Query Type.

Some of the main usage scenarios for query types are:

  • Mapping to database views.
  • Mapping to tables that do not have a primary key defined.
  • Serving as the return type for ad hoc FromSql() queries.
  • Mapping to queries defined in the model.


Mapping a query type to a database view is achieved using the ToTable fluent API.


The following example shows how to use Query Type to query a database view.


You can view this article's sample on GitHub.

First, we define a simple Blog and Post model:

public class Blog
    public int BlogId { get; set; }
    public string Name { get; set; }
    public string Url { get; set; }
    public 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; }

Next, we define a simple database view that will allow us to query the number of posts associated with each blog:

    @"CREATE VIEW View_BlogPostCounts AS 
        SELECT Name, Count(p.PostId) as PostCount from Blogs b
        JOIN Posts p on p.BlogId = b.BlogId
        GROUP BY b.Name");

Next, we define a class to hold the result from the database view:

public class BlogPostsCount
    public string BlogName { get; set; }
    public int PostCount { get; set; }

Next, we configure the query type in OnModelCreating using the modelBuilder.Query<T> API. We use standard fluent configuration APIs to configure the mapping for the Query Type:

protected override void OnModelCreating(ModelBuilder modelBuilder)
        .Property(v => v.BlogName).HasColumnName("Name");

Finally, we can query the database view in the standard way:

var postCounts = db.BlogPostCounts.ToList();

foreach (var postCount in postCounts)
    Console.WriteLine($"{postCount.BlogName} has {postCount.PostCount} posts.");


Note we have also defined a context level query property (DbQuery) to act as a root for queries against this type.