为什么 Kendo 的 ToDataSourceResult 为一个查询创建两个 ADO.Net 调用
Why is Kendo's ToDataSourceResult creating two ADO.Net Calls for one query
我一直在分析与 Kendo 网格小部件一起使用的 MVC 控制器上的读取操作方法,并注意到 Entity Framework 及其查询有一些奇怪的行为。当我直接在 IQueryable
上使用 ToDataSourceResult()
时,EF 上下文会生成一个选择 1(即什么都不做)但包括所有连接子句和过滤器的查询。之后 returns 它会按预期发送实际查询,并应用过滤器和分页等。
注意:这并没有破坏任何东西,我只是在尝试优化,因为我在网格中使用 ServerOperation(true)
和 AJAX,这可能会导致大量调用读取操作和很多这些重复的 ADO.Net 查询(也是因为虚拟调用包含连接子句,对于大型数据集,它实际上可能 运行 慢)。
发生这种情况是否有原因?有没有办法在没有额外调用的情况下仍然对数据库进行服务器分页和过滤(例如 OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY
来自下面的结果,而不是首先使用 linq-to-objects 枚举数据库)?
EntityFramework : v6.2
.Net 框架:v4.7.2
ASP.NetMVC 5
Kendo 2019.1.220
MS SQL 数据库
(缩短)获取重复调用的代码:
public ActionResult Read([DataSourceRequest]DataSourceRequest request)
{
using (var BP01DB = new BP01DBContext())
{
BP01DB.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
var records = (from customer in BP01DB.CUSTOMER_2007
join group2012 in BP01DB.GROUP_2012 on customer.GROUP_CUST_KEY equals group2012.GROUP_CALC_KEY_2012
join cobrCust in BP01DB.COBRCUSTJ_2006 on customer.CUST_CALC_KEY_2007 equals cobrCust.CUST_COBR_KEY
select new InquireCustBranchSalesRecord
{
CobrCustDbKey = cobrCust.COBRCUSTJ_2006_DBKEY,
});
return Json(records.ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}
}
示例输出:
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[CUSTOMER_2007] AS [Extent1]
INNER JOIN [dbo].[GROUP_2012] AS [Extent2] ON [Extent1].[GROUP_CUST_KEY] = [Extent2].[GROUP_CALC_KEY_2012]
INNER JOIN [dbo].[COBRCUSTJ_2006] AS [Extent3] ON [Extent1].[CUST_CALC_KEY_2007] = [Extent3].[CUST_COBR_KEY]
) AS [GroupBy1]
-- Executing at 6/11/2020 4:28:20 PM -05:00
-- Completed in 105 ms with result: SqlDataReader
Closed connection at 6/11/2020 4:28:20 PM -05:00
Opened connection at 6/11/2020 4:28:20 PM -05:00
SELECT
[Extent3].[COBRCUSTJ_2006_DBKEY] AS [COBRCUSTJ_2006_DBKEY]
FROM [dbo].[CUSTOMER_2007] AS [Extent1]
INNER JOIN [dbo].[GROUP_2012] AS [Extent2] ON [Extent1].[GROUP_CUST_KEY] = [Extent2][GROUP_CALC_KEY_2012]
INNER JOIN [dbo].[COBRCUSTJ_2006] AS [Extent3] ON [Extent1].[CUST_CALC_KEY_2007] = [Extent3]. [CUST_COBR_KEY]
ORDER BY row_number() OVER (ORDER BY [Extent3].[COBRCUSTJ_2006_DBKEY] ASC)
OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY
-- Executing at 6/11/2020 4:28:20 PM -05:00
-- Completed in 88 ms with result: SqlDataReader
Closed connection at 6/11/2020 4:28:21 PM -05:00
现在我要做的就是在 ToDataSourceResult()
执行之前枚举 IQueryable
(在本例中添加 .ToList()
),这是适用于小数据集的客户端分页过滤,但这里不行。
var records = (from customer in BP01DB.CUSTOMER_2007
join group2012 in BP01DB.GROUP_2012 on customer.GROUP_CUST_KEY equals group2012.GROUP_CALC_KEY_2012
join cobrCust in BP01DB.COBRCUSTJ_2006 on customer.CUST_CALC_KEY_2007 equals cobrCust.CUST_COBR_KEY
select new InquireCustBranchSalesRecord
{
CobrCustDbKey = cobrCust.COBRCUSTJ_2006_DBKEY,
}).ToList();
并且输出:
Opened connection at 6/11/2020 4:37:24 PM -05:00
SELECT
[Extent3].[COBRCUSTJ_2006_DBKEY] AS [COBRCUSTJ_2006_DBKEY]
FROM [dbo].[CUSTOMER_2007] AS [Extent1]
INNER JOIN [dbo].[GROUP_2012] AS [Extent2] ON [Extent1].[GROUP_CUST_KEY] = [Extent2].[GROUP_CALC_KEY_2012]
INNER JOIN [dbo].[COBRCUSTJ_2006] AS [Extent3] ON [Extent1].[CUST_CALC_KEY_2007] = [Extent3].[CUST_COBR_KEY]
-- Executing at 6/11/2020 4:37:25 PM -05:00
-- Completed in 129 ms with result: SqlDataReader
Closed connection at 6/11/2020 4:37:25 PM -05:00
这个查询
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[CUSTOMER_2007] AS [Extent1]
INNER JOIN [dbo].[GROUP_2012] AS [Extent2] ON [Extent1].[GROUP_CUST_KEY] = [Extent2].[GROUP_CALC_KEY_2012]
INNER JOIN [dbo].[COBRCUSTJ_2006] AS [Extent3] ON [Extent1].[CUST_CALC_KEY_2007] = [Extent3].[CUST_COBR_KEY]
) AS [GroupBy1]
计算行数。 UI 小部件显然会显示结果总数以及第一页。
您可以创建一个存储过程并传递 @Take
和 @Skip
参数,然后在 SQL Server
中进行分页
SELECT *
FROM Sales.SalesOrderHeader
ORDER BY OrderDate
OFFSET (@Skip) ROWS FETCH NEXT (@Take) ROWS ONLY
我一直在分析与 Kendo 网格小部件一起使用的 MVC 控制器上的读取操作方法,并注意到 Entity Framework 及其查询有一些奇怪的行为。当我直接在 IQueryable
上使用 ToDataSourceResult()
时,EF 上下文会生成一个选择 1(即什么都不做)但包括所有连接子句和过滤器的查询。之后 returns 它会按预期发送实际查询,并应用过滤器和分页等。
注意:这并没有破坏任何东西,我只是在尝试优化,因为我在网格中使用 ServerOperation(true)
和 AJAX,这可能会导致大量调用读取操作和很多这些重复的 ADO.Net 查询(也是因为虚拟调用包含连接子句,对于大型数据集,它实际上可能 运行 慢)。
发生这种情况是否有原因?有没有办法在没有额外调用的情况下仍然对数据库进行服务器分页和过滤(例如 OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY
来自下面的结果,而不是首先使用 linq-to-objects 枚举数据库)?
EntityFramework : v6.2
.Net 框架:v4.7.2
ASP.NetMVC 5
Kendo 2019.1.220
MS SQL 数据库
(缩短)获取重复调用的代码:
public ActionResult Read([DataSourceRequest]DataSourceRequest request)
{
using (var BP01DB = new BP01DBContext())
{
BP01DB.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
var records = (from customer in BP01DB.CUSTOMER_2007
join group2012 in BP01DB.GROUP_2012 on customer.GROUP_CUST_KEY equals group2012.GROUP_CALC_KEY_2012
join cobrCust in BP01DB.COBRCUSTJ_2006 on customer.CUST_CALC_KEY_2007 equals cobrCust.CUST_COBR_KEY
select new InquireCustBranchSalesRecord
{
CobrCustDbKey = cobrCust.COBRCUSTJ_2006_DBKEY,
});
return Json(records.ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}
}
示例输出:
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[CUSTOMER_2007] AS [Extent1]
INNER JOIN [dbo].[GROUP_2012] AS [Extent2] ON [Extent1].[GROUP_CUST_KEY] = [Extent2].[GROUP_CALC_KEY_2012]
INNER JOIN [dbo].[COBRCUSTJ_2006] AS [Extent3] ON [Extent1].[CUST_CALC_KEY_2007] = [Extent3].[CUST_COBR_KEY]
) AS [GroupBy1]
-- Executing at 6/11/2020 4:28:20 PM -05:00
-- Completed in 105 ms with result: SqlDataReader
Closed connection at 6/11/2020 4:28:20 PM -05:00
Opened connection at 6/11/2020 4:28:20 PM -05:00
SELECT
[Extent3].[COBRCUSTJ_2006_DBKEY] AS [COBRCUSTJ_2006_DBKEY]
FROM [dbo].[CUSTOMER_2007] AS [Extent1]
INNER JOIN [dbo].[GROUP_2012] AS [Extent2] ON [Extent1].[GROUP_CUST_KEY] = [Extent2][GROUP_CALC_KEY_2012]
INNER JOIN [dbo].[COBRCUSTJ_2006] AS [Extent3] ON [Extent1].[CUST_CALC_KEY_2007] = [Extent3]. [CUST_COBR_KEY]
ORDER BY row_number() OVER (ORDER BY [Extent3].[COBRCUSTJ_2006_DBKEY] ASC)
OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY
-- Executing at 6/11/2020 4:28:20 PM -05:00
-- Completed in 88 ms with result: SqlDataReader
Closed connection at 6/11/2020 4:28:21 PM -05:00
现在我要做的就是在 ToDataSourceResult()
执行之前枚举 IQueryable
(在本例中添加 .ToList()
),这是适用于小数据集的客户端分页过滤,但这里不行。
var records = (from customer in BP01DB.CUSTOMER_2007
join group2012 in BP01DB.GROUP_2012 on customer.GROUP_CUST_KEY equals group2012.GROUP_CALC_KEY_2012
join cobrCust in BP01DB.COBRCUSTJ_2006 on customer.CUST_CALC_KEY_2007 equals cobrCust.CUST_COBR_KEY
select new InquireCustBranchSalesRecord
{
CobrCustDbKey = cobrCust.COBRCUSTJ_2006_DBKEY,
}).ToList();
并且输出:
Opened connection at 6/11/2020 4:37:24 PM -05:00
SELECT
[Extent3].[COBRCUSTJ_2006_DBKEY] AS [COBRCUSTJ_2006_DBKEY]
FROM [dbo].[CUSTOMER_2007] AS [Extent1]
INNER JOIN [dbo].[GROUP_2012] AS [Extent2] ON [Extent1].[GROUP_CUST_KEY] = [Extent2].[GROUP_CALC_KEY_2012]
INNER JOIN [dbo].[COBRCUSTJ_2006] AS [Extent3] ON [Extent1].[CUST_CALC_KEY_2007] = [Extent3].[CUST_COBR_KEY]
-- Executing at 6/11/2020 4:37:25 PM -05:00
-- Completed in 129 ms with result: SqlDataReader
Closed connection at 6/11/2020 4:37:25 PM -05:00
这个查询
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[CUSTOMER_2007] AS [Extent1]
INNER JOIN [dbo].[GROUP_2012] AS [Extent2] ON [Extent1].[GROUP_CUST_KEY] = [Extent2].[GROUP_CALC_KEY_2012]
INNER JOIN [dbo].[COBRCUSTJ_2006] AS [Extent3] ON [Extent1].[CUST_CALC_KEY_2007] = [Extent3].[CUST_COBR_KEY]
) AS [GroupBy1]
计算行数。 UI 小部件显然会显示结果总数以及第一页。
您可以创建一个存储过程并传递 @Take
和 @Skip
参数,然后在 SQL Server
SELECT *
FROM Sales.SalesOrderHeader
ORDER BY OrderDate
OFFSET (@Skip) ROWS FETCH NEXT (@Take) ROWS ONLY