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. mvc_第一遍_业务逻辑层和模型

    常用的动态网页对象: 之前我们提到了,使用request对象可以获得和用户请求相关的一系列信息.这一节,我们来看看另外两个常用对象的常规用途. response对象:用于向客户回应.最常用的用法类似于 ...

  2. element-UI级联选择器(Cascader)获取label值 ,this.$refs['新组件名'].currentLabels 在2.7版本给移除了,新的解决方法。

    原文参考:https://blog.csdn.net/lijiabinbbg/article/details/97396812 遇到的新的问题是如果设置了ref,那么v-model绑定的值不会动态更新 ...

  3. Django--母版

    目录 母版 语法 案例 在之前的两个小程序中,可以发现在写html页面的时候有很多重复的代码 而在python中,为了避免写重复代码,我们通过函数.模块或者类来进行实现,所以在Django里面也有这样 ...

  4. 记一次Spring boot集成mybatis错误修复过程 Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

    最近自己写了一份代码签入到github,然后拉下来运行报下面的错误 Error starting ApplicationContext. To display the conditions repor ...

  5. EnumSet详细讲解

    https://blog.csdn.net/tugangkai/article/details/89631886 之前介绍的Set接口的实现类HashSet/TreeSet,它们内部都是用对应的Has ...

  6. 前端用js获取本地文件的内容

    这里要写成input的形式 调用upload函数 传递的参数就表示所选的文件<input type="file" onchange="upload(this)&qu ...

  7. iOS RACSubject代替通知

    RAC是一个很常用并且很好用的插件,简洁的调用方式可以代替很多原生方法,下面是RACSubject代替通知的使用方式: #import <UIKit/UIKit.h> #import &l ...

  8. 安装部署Spark 1.x Standalone模式集群

    Configuration    spark-env.sh        HADOOP_CONF_DIR=/opt/data02/hadoop-2.6.0-cdh5.4.0/etc/hadoop   ...

  9. Ajax跨域问题及解决方案 asp.net core 系列之允许跨越访问(Enable Cross-Origin Requests:CORS) c#中的Cache缓存技术 C#中的Cookie C#串口扫描枪的简单实现 c#Socket服务器与客户端的开发(2)

    Ajax跨域问题及解决方案   目录 复现Ajax跨域问题 Ajax跨域介绍 Ajax跨域解决方案 一. 在服务端添加响应头Access-Control-Allow-Origin 二. 使用JSONP ...

  10. Python学习日记(四十二) Mysql数据库篇 十

    前言 当我们自己去写SQL代码的时候有时候会因为不熟练会导致效率低,再之后要进行许多的优化,并且操作也较为繁琐.因此ORM框架就能够解决上面的问题,它能根据自身的一些规则来帮助开发者去生成SQL代码. ...