Entity Framework Core Query Types
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
FromSqlmethod 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 Add, Update 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的更多相关文章
- 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 ...
- Professional C# 6 and .NET Core 1.0 - 38 Entity Framework Core
本文内容为转载,重新排版以供学习研究.如有侵权,请联系作者删除. 转载请注明本文出处:Professional C# 6 and .NET Core 1.0 - 38 Entity Framework ...
- 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 ...
- 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 ...
- 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 ...
- 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 ...
- 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 ...
- Entity Framework Core 软删除与查询过滤器
本文翻译自<Entity Framework Core: Soft Delete using Query Filters>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 注意 ...
- Entity Framework Core 执行SQL语句和存储过程
无论ORM有多么强大,总会出现一些特殊的情况,它无法满足我们的要求.在这篇文章中,我们介绍几种执行SQL的方法. 表结构 在具体内容开始之前,我们先简单说明一下要使用的表结构. public clas ...
随机推荐
- golang学习笔记--接口
go 的接口类型用于定义一组行为,其中每个行为都由一个方法声明表示. 接口类型中的方法声明只有方法签名而没有方法实体,而方法签名包括且仅包括方法的名称.参数列表和结果列表. 只要一种数据类型的方法集合 ...
- ubuntu开机自动挂载硬盘
1. 查看硬盘信息 df -h 命令找到目标硬盘(可根据 磁盘分区(路径).分区大小.挂载点 确认/定位 目标) sudo blkid 命令找到目标硬盘的UUID,(关注一下分区的格式化类型,如ex ...
- 可落地的DDD的(2)-为什么说MVC工程架构已经过时
摘要 mvc是一种软件设计模式,最早由Trygve Reenskaug在1978年提出,他有效的解决了表示层,控制器层,逻辑层的代码混合在一起的问题,很好的做到了职责分离.但是在实际的编码实践过程中, ...
- SQL Server的唯一键和唯一索引会将空值(NULL)也算作重复值
我们先在SQL Server数据库中,建立一张Students表: CREATE TABLE [dbo].[Students]( ,) NOT NULL, ) NULL, ) NULL, [Age] ...
- Phoenix连接安全模式下的HBase集群
Phoenix连接安全模式下的HBase集群 HBase集群开启安全模式(即启用kerberos认证)之后,用户无论是用HBase shell还是Phoenix去连接HBase都先需要通过kerber ...
- python基础03--int,bool,str
1.1 数字int 1.i = 100 i.bit_length() 转化为二进制的最小位数 1.2 布尔 bool 1.True False 0是False 1.3 数据转换 ...
- MTSC 2019 深圳站精彩议题第一波更新! | 七五折门票火热售票中
MTSC(中国移动互联网测试开发大会)到今年已经成功举办了五届,这四年里,TesterHome社区一直秉持着务实.能落地.有深度.高质量.重分享的原则,从讲师邀请到内容筛选都严格把控,只为将最能提 ...
- centos逻辑卷使用
要求: 1.硬盘格式成物理卷pvpvcreate/dev/sdb/dev/sda 2.创建卷组vgcreatevg1000/dev/sdb1/dev/sdb2#创建卷组”vg1000” 3.增加卷组 ...
- XenCenter安装VM
XenServer是服务器"虚拟化系统".系统设置为Linux_x86-64即可安装XenServer 和VMware ESX/ESXi有点不同的是,XenServer 不能在Xe ...
- C/ C++ 快速上手
C++ 快速上手 (一)https://www.cnblogs.com/cosmo89929/archive/2012/12/22/2828745.html C++ 快速上手 (二)https://w ...