C# 从多个源筛选、合并、排序和分页数据
目前,我正在通过一种方法从数据库中检索数据,该方法检索C# 从多个源筛选、合并、排序和分页数据,c#,caching,merge,pagination,iqueryable,C#,Caching,Merge,Pagination,Iqueryable,目前,我正在通过一种方法从数据库中检索数据,该方法检索IQueryable,对其进行过滤、排序,然后对其进行分页(所有这些基本上都在数据库中),然后将结果返回到UI以在分页表中显示 我需要集成来自另一个数据库的结果,分页似乎是主要问题 模型相似但不完全相同(相同的字段、不同的名称,在返回之前需要映射到通用域模型) 不可能在DB级别加入 两个DBs之间目前约有1000条记录(在 过去18个月),并可能以大致相同的速度增长(缓慢) 步调 结果总是需要按1-2个字段排序(按日期) 我目前在这两种解
IQueryable
,对其进行过滤、排序,然后对其进行分页(所有这些基本上都在数据库中),然后将结果返回到UI以在分页表中显示
我需要集成来自另一个数据库的结果,分页似乎是主要问题
- 模型相似但不完全相同(相同的字段、不同的名称,在返回之前需要映射到通用域模型)李>
- 不可能在DB级别加入李>
- 两个DBs之间目前约有1000条记录(在 过去18个月),并可能以大致相同的速度增长(缓慢) 步调李>
- 结果总是需要按1-2个字段排序(按日期)
我正在寻找一种性能上合适的算法。理想的解决方案可能是两者的结合(缓存+在DB级别进行过滤),但我目前还没有考虑到这一点。我认为您可以使用以下算法。假设页面大小为10,则对于页面0:
性能应该与查询单个数据库时完全相同,因为对A和B的查询可以并行完成。我在这里创建了一些东西,如果需要,我会回来解释。 我不确定我的算法是否适用于所有的边缘情况,它涵盖了我想到的所有情况,但你永远不知道。我将把代码留在这里让你高兴,我会回答并解释那里做了什么,如果你需要的话,请留下评论 并使用值之间存在较大差距的项目列表执行多个测试
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
//each time when this objects are accessed, consider as a database call
private static IQueryable<model1> dbsetModel_1;
private static IQueryable<model2> dbsetModel_2;
private static void InitDBSets()
{
var rnd = new Random();
List<model1> dbsetModel1 = new List<model1>();
List<model2> dbsetModel2 = new List<model2>();
for (int i = 1; i < 300; i++)
{
if (i % 2 == 0)
{
dbsetModel1.Add(new model1() { Id = i, OrderNumber = rnd.Next(1, 10), Name = "Test " + i.ToString() });
}
else
{
dbsetModel2.Add(new model2() { Id2 = i, OrderNumber2 = rnd.Next(1, 10), Name2 = "Test " + i.ToString() });
}
}
dbsetModel_1 = dbsetModel1.AsQueryable();
dbsetModel_2 = dbsetModel2.AsQueryable();
}
public static void Main()
{
//generate sort of db data
InitDBSets();
//test
var result2 = GetPage(new PagingFilter() { Page = 5, Limit = 10 });
var result3 = GetPage(new PagingFilter() { Page = 6, Limit = 10 });
var result5 = GetPage(new PagingFilter() { Page = 7, Limit = 10 });
var result6 = GetPage(new PagingFilter() { Page = 8, Limit = 10 });
var result7 = GetPage(new PagingFilter() { Page = 4, Limit = 20 });
var result8 = GetPage(new PagingFilter() { Page = 200, Limit = 10 });
}
private static PagedList<Item> GetPage(PagingFilter filter)
{
int pos = 0;
//load only start pages intervals margins from both database
//this part need to be transformed in a stored procedure on db one, skip, take to return interval start value for each frame
var framesBordersModel1 = new List<Item>();
dbsetModel_1.OrderBy(x => x.Id).ThenBy(z => z.OrderNumber).ToList().ForEach(i => {
pos++;
if (pos - 1 == 0)
{
framesBordersModel1.Add(new Item() { criteria1 = i.Id, criteria2 = i.OrderNumber, model = i });
}
else if ((pos - 1) % filter.Limit == 0)
{
framesBordersModel1.Add(new Item() { criteria1 = i.Id, criteria2 = i.OrderNumber, model = i });
}
});
pos = 0;
//this part need to be transformed in a stored procedure on db two, skip, take to return interval start value for each frame
var framesBordersModel2 = new List<Item>();
dbsetModel_2.OrderBy(x => x.Id2).ThenBy(z => z.OrderNumber2).ToList().ForEach(i => {
pos++;
if (pos - 1 == 0)
{
framesBordersModel2.Add(new Item() { criteria1 = i.Id2, criteria2 = i.OrderNumber2, model = i });
}
else if ((pos -1) % filter.Limit == 0)
{
framesBordersModel2.Add(new Item() { criteria1 = i.Id2, criteria2 = i.OrderNumber2, model = i });
}
});
//decide where is the position of your cursor based on start margins
//int mainCursor = 0;
int cursor1 = 0;
int cursor2 = 0;
//filter pages start from 1, filter.Page cannot be 0, if indeed you have page 0 change a lil' bit he logic
if (framesBordersModel1.Count + framesBordersModel2.Count < filter.Page) throw new Exception("Out of range");
while ( cursor1 + cursor2 < filter.Page -1)
{
if (framesBordersModel1[cursor1].criteria1 < framesBordersModel2[cursor2].criteria1)
{
cursor1++;
}
else if (framesBordersModel1[cursor1].criteria1 > framesBordersModel2[cursor2].criteria1)
{
cursor2++;
}
//you should't get here case main key sound't be duplicate, annyhow
else
{
if (framesBordersModel1[cursor1].criteria2 < framesBordersModel2[cursor2].criteria2)
{
cursor1++;
}
else
{
cursor2++;
}
}
//mainCursor++;
}
//magic starts
//inpar skipable
int skipEndResult = 0;
List<Item> dbFramesMerged = new List<Item>();
if ((cursor1 + cursor2) %2 == 0)
{
dbFramesMerged.AddRange(
dbsetModel_1.OrderBy(x => x.Id)
.ThenBy(z => z.OrderNumber)
.Skip(cursor1*filter.Limit)
.Take(filter.Limit)
.Select(x => new Item() {criteria1 = x.Id, criteria2 = x.OrderNumber, model = x})
.ToList()); //consider as db call EF or Stored Procedure
dbFramesMerged.AddRange(
dbsetModel_2.OrderBy(x => x.Id2)
.ThenBy(z => z.OrderNumber2)
.Skip(cursor2*filter.Limit)
.Take(filter.Limit)
.Select(x => new Item() {criteria1 = x.Id2, criteria2 = x.OrderNumber2, model = x})
.ToList());
; //consider as db call EF or Stored Procedure
}
else
{
skipEndResult = filter.Limit;
if (cursor1 > cursor2)
{
cursor1--;
}
else
{
cursor2--;
}
dbFramesMerged.AddRange(
dbsetModel_1.OrderBy(x => x.Id)
.ThenBy(z => z.OrderNumber)
.Skip(cursor1 * filter.Limit)
.Take(filter.Limit)
.Select(x => new Item() { criteria1 = x.Id, criteria2 = x.OrderNumber, model = x })
.ToList()); //consider as db call EF or Stored Procedure
dbFramesMerged.AddRange(
dbsetModel_2.OrderBy(x => x.Id2)
.ThenBy(z => z.OrderNumber2)
.Skip(cursor2 * filter.Limit)
.Take(filter.Limit)
.Select(x => new Item() { criteria1 = x.Id2, criteria2 = x.OrderNumber2, model = x })
.ToList());
}
IQueryable<Item> qItems = dbFramesMerged.AsQueryable();
PagedList<Item> result = new PagedList<Item>();
result.AddRange(qItems.OrderBy(x => x.criteria1).ThenBy(z => z.criteria2).Skip(skipEndResult).Take(filter.Limit).ToList());
//here again you need db cals to get total count
result.Total = dbsetModel_1.Count() + dbsetModel_2.Count();
result.Limit = filter.Limit;
result.Page = filter.Page;
return result;
}
}
public class PagingFilter
{
public int Limit { get; set; }
public int Page { get; set; }
}
public class PagedList<T> : List<T>
{
public int Total { get; set; }
public int? Page { get; set; }
public int? Limit { get; set; }
}
public class Item : Criteria
{
public object model { get; set; }
}
public class Criteria
{
public int criteria1 { get; set; }
public int criteria2 { get; set; }
//more criterias if you need to order
}
public class model1
{
public int Id { get; set; }
public int OrderNumber { get; set; }
public string Name { get; set; }
}
public class model2
{
public int Id2 { get; set; }
public int OrderNumber2 { get; set; }
public string Name2 { get; set; }
}
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
命名空间控制台应用程序1
{
班级计划
{
//每次访问这些对象时,将其视为数据库调用
私有静态可查询dbsetModel_1;
私有静态可查询dbsetModel_2;
私有静态void InitDBSets()
{
var rnd=新随机数();
List dbsetModel1=新列表();
List dbsetModel2=新列表();
对于(int i=1;i<300;i++)
{
如果(i%2==0)
{
添加(newmodel1(){Id=i,OrderNumber=rnd.Next(1,10),Name=“Test”+i.ToString()});
}
其他的
{
添加(newmodel2(){Id2=i,OrderNumber2=rnd.Next(1,10),Name2=“Test”+i.ToString()});
}
}
dbsetModel_1=dbsetModel1.AsQueryable();
dbsetModel_2=dbsetModel2.AsQueryable();
}
公共静态void Main()
{
//生成数据库数据的排序
InitDBSets();
//试验
var result2=GetPage(new PagingFilter(){Page=5,Limit=10});
var result3=GetPage(new PagingFilter(){Page=6,Limit=10});
var result5=GetPage(新分页过滤器(){Page=7,Limit=10});
var result6=GetPage(new PagingFilter(){Page=8,Limit=10});
var result7=GetPage(new PagingFilter(){Page=4,Limit=20});
var result8=GetPage(new PagingFilter(){Page=200,Limit=10});
}
专用静态页面列表GetPage(PagingFilter筛选器)
{
int pos=0;
//仅从两个数据库加载起始页和页边距
//这部分需要在db one、skip、take上的存储过程中进行转换,以返回每帧的间隔起始值
var framesBordersModel1=新列表();
dbsetModel_1.OrderBy(x=>x.Id).ThenBy(z=>z.OrderNumber).ToList().ForEach(i=>{
pos++;
如果(位置-1==0)
{
framesBordersModel1.Add(新项(){criteria1=i.Id,criteria2=i.OrderNumber,model=i});
}
如果((位置-1)%filter.Limit==0),则为else
{
framesBordersModel1.Add(新项(){criteria1=i.Id,criteria2=i.OrderNumber,model=i});
}
});
pos=0;
//这一部分需要在dbtwo、skip、take上的存储过程中进行转换,以返回每个帧的间隔开始值
var framesBordersModel2=新列表();
dbsetModel_2.Ord