This feature was added in EF Core 2.1

Query types are non-entity types (classes) that form part of the conceptual model and can be mapped to tables and views that don't have an identity column specified, or to a DbQuery type. As such, they can be used in a number of scenarios:

  • They can represent ad hoc types returned from FromSql method calls
  • They enable mapping to views
  • They enable mapping to tables that do not have an Identity column

Importantly, since they do not need to have a key value specified, query types do not participate in change tracking and cannot take part in AddUpdate or Delete operations. They essentially represent read-only objects.

Mapping to DbQuery link

The DbQuery type was introduced in EF Core 2.1. along with query types. A DbQuery is a property on the DbContext that acts in a similar way to a DbSet, providing a root for LINQ queries. However, it doesn't enable operations that write to the database e.g. Add. The DbQuery maps to a table or view. To illustrate how the DbQuery works, here is a simple model representing customers and their orders:

public class Customer
{
public int CustomerId { get; set; }
public string Name { get; set; }
public ICollection<Order> Orders { get; set; } = new HashSet<Order>();
} public class Order
{
public int OrderId { get; set; }
public DateTime DateCreated { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
public ICollection<OrderItem> OrderItems { get; set; } = new HashSet<OrderItem>();
} public class OrderItem
{
public int OrderItemId { get; set; }
public string Item { get; set; }
public decimal Price { get; set; }
}

In the real world, the model will have a lot more properties and any queries against the respective DbSet objects will return all columns of data. There will be occasions where just a subset of columns are required, as defined in the following database view named OrderHeaders:

create view OrderHeaders as
select c.Name as CustomerName,
o.DateCreated,
sum(oi.Price) as TotalPrice,
count(oi.Price) as TotalItems
from OrderItems oi
inner join Orders o on oi.OrderId = o.OrderId
inner join Customers c on o.CustomerId = c.CustomerId
group by oi.OrderId, c.Name, o.DateCreated

The data returned from calling the view is represented by the following query type:

public class OrderHeader
{
public string CustomerName { get; set; }
public DateTime DateCreated { get; set; }
public int TotalItems { get; set; }
public decimal TotalPrice { get; set; }
}

The OrderHeader class is mapped to the view via the DbQuery<OrderHeader> property that's added to the DbContext along with the DbSet properties for the orders, orderitems and the customers:

public class SampleContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public DbSet<OrderItem> OrderItems { get; set; }
public DbSet<Customer> Customers { get; set; } public DbQuery<OrderHeader> OrderHeaders { get; set; } ...
}

This is all that is required to enable querying of data in the same way as if the OrderHeaders property was a DbSet:

var orderHeaders = db.OrderHeaders.ToList();

As with normal DbSet queries, you can also specify filter criteria:

var orderHeaders = db.OrderHeaders.Where(x => x.TotalItems > 15).ToList();

Configuration link

If you don't want to clutter up your DbContext with multiple DbQuery properties as well as the DbSet declarations, you have two options. One is to make your DbContext class a partial class and then split the DbQuery declarations into a separate file:

[SampleContext.cs]
public partial class SampleContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public DbSet<OrderItem> OrderItems { get; set; }
public DbSet<Customer> Customers { get; set; } ...
}
[SampleContextDbQuery.cs]
public partial class SampleContext : DbContext
{
public DbQuery<OrderHeader> OrderHeaders { get; set; }
public DbQuery<OrderTotal> OrderTotals { get; set; }
...
}

The other option is to use configuration to include the query objects in the conceptual model using the ModelBuilder.Query method:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Query<OrderHeader>().ToView("OrderHeaders");
}

Now there is no DbQuery type object to base your query from, so you use the DbContext.Query<TEntity> method instead:

var orderHeaders = db.Query<OrderHeader>().ToList();

Relationships link

It is possible for query types to take part in relationships as a navigational property, but the query type cannot form the principal in the relationship. The key property of the principal end of the relationship needs to be included in the table or view represented by the query type so that a SQL join can be made:

select      c.Name as CustomerName,
c.CustomerId,
o.DateCreated,
sum(oi.Price) as TotalPrice,
count(oi.Price) as TotalItems
from OrderItems oi
inner join Orders o on oi.OrderId = o.OrderId
inner join Customers c on o.CustomerId = c.CustomerId
group by oi.OrderId, c.Name, o.DateCreated

Then the Customer entity must be included as a navigational property:

public class OrderHeader
{
public string CustomerName { get; set; }
public DateTime DateCreated { get; set; }
public int TotalItems { get; set; }
public decimal TotalPrice { get; set; }
public Customer Customer { get; set; }
}

This is enough to enable the Include method to eager load the principal entity:

var orderHeaders = db.Query<OrderHeader>().Include(o => o.Customer).ToList();

FromSql link

You can use a query type to return non-entity types from FromSql method calls more efficiently than the previous approach that required you to query an entity type and then project a subset of the properties to a different type.

The query type must be registered with the DbContext as a DbQuery and you must include columns in the SQL to match all of the properties in the query type:

var orderHeaders = db.OrderHeaders.FromSql(
@"select c.Name as CustomerName, o.DateCreated, sum(oi.Price) as TotalPrice,
count(oi.Price) as TotalItems
from OrderItems oi
inner join Orders o on oi.OrderId = o.OrderId
inner join Customers c on o.CustomerId = c.CustomerId
group by oi.OrderId, c.Name, o.DateCreated");

If one of the properties of the query type is a complex type, such as the Customer property in the previous example, you must include columns for all of the complex type's properties, too.

The main benefit of this approach is that you do not have to create a View or Table in the database to represent a non-entity type.

Entity Framework Core Query Types的更多相关文章

  1. Working with Data » Getting started with ASP.NET Core and Entity Framework Core using Visual Studio » 增、查、改、删操作

    Create, Read, Update, and Delete operations¶ 5 of 5 people found this helpful By Tom Dykstra The Con ...

  2. Professional C# 6 and .NET Core 1.0 - 38 Entity Framework Core

    本文内容为转载,重新排版以供学习研究.如有侵权,请联系作者删除. 转载请注明本文出处:Professional C# 6 and .NET Core 1.0 - 38 Entity Framework ...

  3. Professional C# 6 and .NET Core 1.0 - Chapter 38 Entity Framework Core

    本文内容为转载,重新排版以供学习研究.如有侵权,请联系作者删除. 转载请注明本文出处:Professional C# 6 and .NET Core 1.0 - Chapter 38 Entity F ...

  4. Working with Data » Getting started with ASP.NET Core and Entity Framework Core using Visual Studio » 读取关系数据

    Reading related data¶ 9 of 9 people found this helpful The Contoso University sample web application ...

  5. Working with Data » Getting started with ASP.NET Core and Entity Framework Core using Visual Studio » 创建复杂数据模型

    Creating a complex data model 创建复杂数据模型 8 of 9 people found this helpful The Contoso University sampl ...

  6. Working with Data » Getting started with ASP.NET Core and Entity Framework Core using Visual Studio » 排序、筛选、分页以及分组

    Sorting, filtering, paging, and grouping 7 of 8 people found this helpful By Tom Dykstra The Contoso ...

  7. Working with Data » 使用Visual Studio开发ASP.NET Core MVC and Entity Framework Core初学者教程

    原文地址:https://docs.asp.net/en/latest/data/ef-mvc/intro.html The Contoso University sample web applica ...

  8. Entity Framework Core 软删除与查询过滤器

    本文翻译自<Entity Framework Core: Soft Delete using Query Filters>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 注意 ...

  9. Entity Framework Core 执行SQL语句和存储过程

    无论ORM有多么强大,总会出现一些特殊的情况,它无法满足我们的要求.在这篇文章中,我们介绍几种执行SQL的方法. 表结构 在具体内容开始之前,我们先简单说明一下要使用的表结构. public clas ...

随机推荐

  1. 『金字塔 区间dp』

    金字塔 Description 虽然探索金字塔是极其老套的剧情,但是这一队 探险家还是到了某金字塔脚下.经过多年的研究,科 学家对这座金字塔的内部结构已经有所了解.首先, 金字塔由若干房间组成,房间之 ...

  2. 『磁力块 bfs 分块』

    磁力块 Description 在一片广袤无垠的原野上,散落着N 块磁石.每个磁石的性质可以用一个五元组 (x,y,m,p,r)描述,其中x,y 表示其坐标,m 是磁石的质量,p 是磁力,r 是吸引半 ...

  3. 什么是SQL ?

    SQL 1.什么是SQL ? Structured Query Languange:结构化查询语言 其实就是定义了操作所有关系型数据库的规则.每一种数据库操作的方式存在不一样的地方,称为“方言”. 2 ...

  4. 概述UML——UML系列篇一

    前言 作为Java应用开发者,日益感觉到对象建模的重要性.系统的复杂性,对于不能全局掌握的我和编程时没有对象模型指导时,编写实现代码时,感觉甚是困难.处于这些原因,这里想借助学习UML建模,在分析需求 ...

  5. MATLAB datenum日期转换为Python日期

    摘要 MATLAB datenum时间格式参数众多,本文只简单关注 units 参数,即基准年份和计时度量(天.小时). 命令行演示在 ipython 和 Octave 中进行. 示例1:小时制,基准 ...

  6. SQL Server中临时表是在什么schema下的(转载)

    Specifying schema for temporary tables 问: I'm used to seeing temporary tables created with just the ...

  7. H5+asp.net 微信开发 遇到过的坑

    一.微信授权登录 1. 根据code 获取_access_tokens 2. 根据取到的openid和_access_tokens获取用户信息最神奇的是我用我自己的微信账号测试,一开始还可以取到tok ...

  8. Ubuntu系统下基于docker部署Jenkins环境

    本文是在ubuntu环境下安装jenkins,jenkins运行在docker容器中, 至于docker如何安装,请参考https://www.cnblogs.com/xingyunqiu/p/115 ...

  9. Java NIO , AIO

    New IO: 特点:不再阻塞 Channel , Buffer Async IO: 特点:异步

  10. prometheus搜索指标显示No datapoints found.

    在指标能够在下拉框可以选择到的情况下,还有No datapoints found. 则考虑是时区的问题,详见官方issue https://github.com/prometheus/promethe ...