实体框架——提高检索大量数据的查询效率



我有一个数据库与大量的数据- Excel文件管理。

当每个对象包含一个Excel文件(工作表数,每个工作表的行列表)时,应用程序管理对象。

应用程序包含一个数据网格和一个表列表。用户将选择修订号,和工作表名称,显示同一工作表的行。

对象是这样构建的:

Version对象包含page的列表,每个page包含PageLine的列表。

检索数据的最佳方法是什么?

例如,我的PopulateGrid方法:
public void PopulateGrid() 
{
CurrentPageLineGridObjects.Clear();
PreviousPageLineGridObjects.Clear();
SetCurrentConnectorPageList();
// get current revision
CurrentPageLineGridObjects = CurrentCombinedPageList.Where(page => page.Name == 
PageNameSelected).FirstOrDefault().PageLines.ToList().ToObservablePageLineGridObjectCollection();
//get prev revision
RevisionCOMBINED prevRevCombined = pgroupDataService.GetRevisionCombinedForPGroup(((PGroup)PGroupSelected.Object).Id).Result;
// get pages and pagelines for revision eeprom and override.
List<Page> eepromPages =  
revisionEEPROMDataService.GetEEPROMPages(prevRevCombined.RevisionEEPROM.Id).Result;                    
}
public async Task<List<Page>> GetEEPROMPages(int eepromRevId)
{
string[] includes = { "Pages", "Pages.PageLines" };
IEnumerable<RevisionEEPROM> list = (IEnumerable<RevisionEEPROM>)await dataService.GetAll(includes);
return list.Where(r => r.Id == eepromRevId).SelectMany(p => p.Pages).ToList();
}
public async Task<IEnumerable<T>> GetAll()
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
IEnumerable<T> entities = await contex.Set<T>().ToListAsync();
return entities;
}
}

正如你所看到的,我把所有的版本数据连同所有的表和所有的PageLines一起拉出来,然后只按给定的版本键进行过滤。

加载花了我不少时间。

如有任何建议,我将不胜感激。

我尝试使用IQueryable:

public async Task<List<T>> GetQueryable(string[] includes = null)
{
using (DeployToolDBContex context = _contextFactory.CreateDbContext())
{
if (includes != null)
{
var query = context.Set<T>().AsQueryable();
foreach (var include in includes)
query = query.Include(include);
return query.ToList();
}
else
{
List<T> entities = await context.Set<T>().AsQueryable().ToListAsync();
return entities;
}
}
}

这是EF的糟糕用法。首先,像这样的代码:

IEnumerable<RevisionEEPROM> list = (IEnumerable<RevisionEEPROM>)await dataService.GetAll(includes);
return list.Where(r => r.Id == eepromRevId).SelectMany(p => p.Pages).ToList();

在过滤之前,将整个表和相关的包含(基于传入的包含数组)提取到内存中。

假设您正在使用using块在该数据服务方法中限定DbContext的作用域,那么最好的选择是引入GetPagesForEepromRevision()方法来获取数据服务中给定ID的页面。此数据服务的通用实现应该是这些数据服务的基类,以便它们可以提供通用功能,但可以扩展以支持特定情况,以优化每个领域的查询。例如,如果你有:

public class DataService<T>
{
public async Task<IEnumerable<T>> GetAll() {...}
// ...
}

使用:

public class EepromDataService : DataService<EEPROM>
{
public async Task<IEnumerable<Page>> GetPagesForEepromRevision(int eepromRevId)
{
using (DeployToolDBContext context = _contexFactory.CreateDbContext())
{
var pages = await context.Set<EEPROM>()
.Where(x => x.Id == eepromRevId)
.SelectMany(x => x.Pages)
.ToListAsync();
return pages;
}
}
}

如果你的调用代码创建的是var dataService = new DataService<EEPROM>();之类的东西,那么它就会变成var dataService = new EepromDataService();

前面提到的IQueryable选项:

public IQueryable<T> GetQueryable()
{
var query = _context.Set<T>().AsQueryable();
return query;
}

当你去取你的数据时:

var results = await dataService.GetQueryable()
.Where(r => r.Id == eepromRevId)
.SelectMany(r => r.Pages)
.ToListAsync();
return results;

这需要一个工作单元模式(Unit of Work pattern),它将DbContext限定在消费者级别(例如:GetEEPROMPages方法),或者一个共享依赖注入DbContext,它将跨越调用ToListAsync的调用者以及数据服务。由于您的示例使用using块将DbContext限定在dataService内部,这可能是一个更大的变化。

总的来说,你需要检查异步调用和同步调用的使用,因为其他方法做的事情像:

RevisionCOMBINED prevRevCombined = pgroupDataService.GetRevisionCombinedForPGroup(((PGroup)PGroupSelected.Object).Id).Result;

只是调用.Result是非常糟糕的做法。如果您需要在同步方法中调用异步调用,那么有适当的方法可以做到这一点,并确保可以发生异常冒泡之类的事情。例如,参见(如何在c#中从同步方法调用异步方法?)如果代码不需要异步,那么就让它保持同步。async不是银色的"走得更快";总之,只要代码实际上是在整个过程中利用async编写的,它就可以使支持代码响应更快。(即ASP.Net中的HTTP Web请求)

最新更新