我一直在与Kendo Grid小部件一起使用的MVC控制器上分析Read动作方法,并注意到实体框架及其查询存在一些奇怪的行为。当我直接在ToDataSourceResult()
上使用IQueryable
时,EF上下文会生成一个查询,该查询选择1(不执行任何操作),但包括所有连接子句和过滤器。返回之后,它会按预期发送实际查询,并应用过滤器和分页等。
注意:这没有任何破坏,我只是在尝试优化,因为我在网格中将Ajax与ServerOperation(true)
一起使用,这可能导致对Read操作的调用以及许多重复的ADO.Net查询(还因为虚拟调用包含连接子句,对于大型数据集,它实际上可能运行缓慢)
是否有发生这种情况的原因?有没有任何方法可以在没有额外调用的情况下仍然对数据库进行服务器分页和过滤(例如,从下面的结果中获取OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY
而不是先使用linq-to-objects枚举数据库)?
EntityFramework:v6.2.Net Framework:v4.7.2ASP.Net MVC 5剑道2019.1.220MS 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
现在我要摆脱第一个无关查询所需要做的就是在IQueryable
之前枚举ToDataSourceResult()
(在这种情况下,通过添加.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小部件显然可以显示结果总数以及第一页。