这是我的代码:
// grid query
var data = ctx.spRewards(Month, Year, null, MedicalID).Select(p => new
{
MedicalID = p.MedicalID,
DateReward = p.DateReward,
Medical = p.Medical,
AmountRefunds = p.AmountRefunds,
AmountActivitiesStart = p.AmountActivitiesStart,
AmountActivitiesEnd = p.AmountActivitiesEnd,
AmountActivities = p.AmountActivities,
AmountTotal = p.AmountTotal,
Month = p.Month,
Year = p.Year
});
// some further filters that will be attached in case
// grid order
data = data.OrderBy(p => p.DateReward).ThenBy(p => p.MedicalID);
// grid data
var dataTotal = data.Count();
if (formData.length >= 0)
{
data = data.Skip(formData.start).Take(formData.length);
}
var dataFiltered = data.ToList();
return Json(new { data = dataFiltered, recordsFiltered = dataTotal, recordsTotal = dataTotal });
但是一旦我尝试执行
var dataFiltered = data.ToList();
,我就会得到一个查询的结果不能被枚举多次。
我的目的是首先仅返回记录数(仅获取过滤后的数据量,而不下载内存中的所有记录,然后进行计数,这需要时间和资源),然后使用 Skip/Take 对其进行分页。
通常这可以在桌子上使用
IQueryable<a>
来实现:
var data = ctx.SomeTable.AsNoTracking().Select(p => new
{
//
})
但它不会直接调用数据库中的存储过程。我尝试使用
data
将 IEnumerable<a>
从 IQueryable<a>
转换为 ctx.spRewards(Month, Year, null, MedicalID).AsQueryable()
,但出现了同样的错误。
我需要配置什么?
编辑:添加了答案建议的整个“实际”代码,但仍然不起作用:
var dataQuery = ctx.spRewards(Month, Year, null, MedicalID).AsQueryable().Select(p => new
{
MedicalID = p.MedicalID,
DateReward = p.DateReward,
Medical = p.Medical,
AmountRefunds = p.AmountRefunds,
AmountActivitiesStart = p.AmountActivitiesStart,
AmountActivitiesEnd = p.AmountActivitiesEnd,
AmountActivities = p.AmountActivities,
AmountTotal = p.AmountTotal,
Month = p.Month,
Year = p.Year
});
// grid - filters
string searchValue = Request.Form.GetValues("search[value]")?.FirstOrDefault()?.ToLower();
if (!string.IsNullOrEmpty(searchValue))
{
dataQuery = dataQuery.Where(p =>
p.Medical.ToLower().Contains(searchValue) ||
p.AmountRefunds.ToString().ToLower().Contains(searchValue) ||
p.AmountActivitiesStart.ToString().ToLower().Contains(searchValue) ||
p.AmountActivitiesEnd.ToString().ToLower().Contains(searchValue) ||
p.AmountTotal.ToString().ToLower().Contains(searchValue)
);
}
// grid - order
string orderColumnId = Request.Form.GetValues("order[0][column]")?.FirstOrDefault();
string orderColumn = Request.Form.GetValues("columns[" + orderColumnId + "][data]")?.FirstOrDefault();
string orderDir = Request.Form.GetValues("order[0][dir]")?.FirstOrDefault();
if (!string.IsNullOrEmpty(orderColumn))
{
if (orderDir == "desc")
{
dataQuery = dataQuery.OrderByDescending(orderColumn);
}
else
{
dataQuery = dataQuery.OrderBy(orderColumn);
}
}
else
{
dataQuery = dataQuery.OrderBy(p => p.DateReward).ThenBy(p => p.MedicalID);
}
// grid - result
var dataClone = dataQuery.CloneQuery();
var dataTotal = dataQuery.Count();
if (formData.length >= 0)
{
dataClone = dataClone.Skip(formData.start).Take(formData.length);
}
var dataFiltered = dataClone.ToList();
return Json(new { data = dataFiltered, recordsFiltered = dataTotal, recordsTotal = dataTotal });
编辑 2:添加了 spRewards 定义:
public virtual ObjectResult<spRewards_Result> spRewards(Nullable<int> month, Nullable<int> year, Nullable<int> clinicID, Nullable<int> medicalID)
{
var monthParameter = month.HasValue ?
new ObjectParameter("Month", month) :
new ObjectParameter("Month", typeof(int));
var yearParameter = year.HasValue ?
new ObjectParameter("Year", year) :
new ObjectParameter("Year", typeof(int));
var clinicIDParameter = clinicID.HasValue ?
new ObjectParameter("ClinicID", clinicID) :
new ObjectParameter("ClinicID", typeof(int));
var medicalIDParameter = medicalID.HasValue ?
new ObjectParameter("MedicalID", medicalID) :
new ObjectParameter("MedicalID", typeof(int));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<spRewards_Result>("spRewards", monthParameter, yearParameter, clinicIDParameter, medicalIDParameter);
}
我认为你的问题是你正在使用带有实体框架的存储过程,它创建了一个仅向前的结果集。
使用扩展方法,您可以创建具有相同条件的新查询:
public static class IQueryableExt
{
public static IQueryable<T> CloneQuery<T>(this IQueryable<T> q) => q.Provider.CreateQuery<T>(q.Expression);
}
然后您可以使用原始查询来获取总计数,使用克隆查询来获取过滤后的记录:
// grid data
var data2 = data.CloneQuery();
var dataTotal = data.Count();
if (formData.length >= 0)
{
data2 = data2.Skip(formData.start).Take(formData.length);
}
var dataFiltered = data2.ToList();
select @@ROWCOUNT
并查询 第二个结果集(其中包含行数)。
正如您在上面的链接中看到的,这可以通过调用来实现
reader.NextResult();
这会将读取器的上下文切换到下一个结果集。如果您这样做而不读取第一个结果集,不应该传输任何行数据。
如果您只是调用它而不前进到第二个结果集,它将默认返回存储过程执行中的数据。
示例 - Northwind 数据库。 在这里,我们在 Northwind 数据库中创建一个新的存储过程用于演示目的,调用
[dbo][Employee Sales by Country]
并添加第二个结果集:
CREATE PROCEDURE [dbo].[GetEmployeeSalesCount]
@Beginning_Date DateTime, @Ending_Date DateTime
AS
BEGIN
SET NOCOUNT ON;
-- first resultset
EXECUTE [dbo].[Employee Sales by Country]
@Beginning_Date,@Ending_Date
-- 2nd resultset
select @@ROWCOUNT as count
END
注意: 您只需将
select @@ROWCOUNT as count
添加到现有存储过程 (SP) 的末尾,就在要作为数据返回的最后一个选择之后。
无需在生产代码中创建第二个 SP,这仅用于演示。
现在您只想根据传递的参数获取 GetEmployeeSalesCount 的行数。
这是检索行数的 C# 代码:
// use LinqPad to try it out, connecting with the Northwind database
void Main()
{
var conn = this;
var dtFrom = new DateTime(1998, 1, 1);
var dtTo = new DateTime(1999, 1, 1);
var count = GetSalesRowCount(conn.Connection, dtFrom, dtTo);
Console.WriteLine($"Rowcount: {count}");
// now you can do filtering based on the count value
// This will read from the 1st resultset
var skipNrRows = 260;
var query = conn.GetEmployeeSalesCount(dtFrom, dtTo).AsDynamic()
.Skip(skipNrRows).Take(count - skipNrRows);
query.Dump();
}
int GetSalesRowCount(IDbConnection conn, DateTime fromDate, DateTime toDate)
{
var cmd = conn.CreateCommand();
cmd.CommandText = "[dbo].[GetEmployeeSalesCount]";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@Beginning_Date", SqlDbType.DateTime) { Value = new DateTime(1998, 1, 1) });
cmd.Parameters.Add(new SqlParameter("@Ending_Date", SqlDbType.DateTime) { Value = new DateTime(1999, 1, 1) });
cmd.Connection.Open();
var reader = cmd.ExecuteReader();
// this will read from the 2nd resultset
reader.NextResult(); reader.Read();
var count = (int)reader["count"];
cmd.Connection.Close();
return count;
}
打印:
注意: 您还可以通过这种方式访问第二个结果集:
var queryForRS2 = conn.GetEmployeeSalesCount(dtFrom, dtTo).CreateDataReader();
queryForRS2.NextResult(); queryForRS2.Read();
var count = (int)queryForRS2.GetValue(0);