c# - 优化咨询服务
问题描述
我在带有 linq 的 DataGridView 中进行了以下搜索,但搜索速度太慢,如何优化此查询或为什么会发生这种情况?DataGridView 中有 800 条记录
private void txtBusqueda_TextChanged(object sender, EventArgs e)
{
var filtro = (from p in Global.ArticulosGlobales
where p.codigo.ToUpper().StartsWith(txtBusqueda.Text.ToUpper())
select p).ToList();
dtGridDatos.DataSource = filtro;
}
解决方案
链接到实体
在查询中对列使用函数会使谓词不可 SARGable(如果您有物化视图或索引,则有例外,但我认为这在这种情况下不适用)。
尽管如此,鉴于 ISO SQL 默认情况下不区分大小写,这意味着您根本不需要执行大小写转换,因为 Linq 查询中的所有字符串比较都将不区分大小写,除非您使用的是非默认排序规则,即明确区分大小写。
还要确保您对codigo
列进行了索引,以便前缀搜索是最佳的:
CREATE INDEX IX_codigo ON dbo.ArticulosGlobales ( codigo );
所以这会做:
String prefix = this.txtBusqueda.Text;
var filtro = await Global.ArticulosGlobales
.Where( p => p.codigo.StartsWith( prefix ) )
.ToListAsync()
.ConfigureAwait(true); // <-- Explicit continuation on UI thread.
dtGridDatos.DataSource = filtro;
Linq 到对象
与 Linq-to-Entities 一样, usingToUpper()
是执行不区分大小写的字符串比较的最糟糕方法 - 在 lambda 中使用它也意味着过多的字符串分配,这是一件坏事。相反,使用StringComparer.OrdinalIgnoreCase
(或者StringComparison.OrdinalIgnoreCase
如果它是一个参数)。
此外,保存this.txtBusqueda.Text
到一个临时变量,因为.Text
访问TextBox
器是非平凡的并执行线程访问检查,这进一步减慢了速度。
String prefix = this.txtBusqueda.Text;
var filtro = Global.ArticulosGlobales
.Where( p => p.codigo.StartsWith( prefix, StringComparison.OrdinalIgnoreCase ) )
.ToList()
dtGridDatos.DataSource = filtro;
进一步优化:缓存之前的结果
我假设这是一个 find-as-you-type 过滤器 - 在这种情况下请注意,当用户输入一个新字母时,每个后续结果集都是前一个结果集的子集,这意味着您只需要访问数据库一次并将这些结果存储在内存中(当然,将它们存储在前缀搜索优化的结构中)。然后,所有后续更具体的搜索将对最后获得的子集进行操作。
一个初始优化,仍然可以极大地改进,可能如下所示:
private readonly Dictionary< String, List<ArticulosGlobales > > searchSubsets = new Dictionary< String, List<ArticulosGlobales > >( StringComparer.OrdinalIgnoreCase );
private String connectionString = "...";
private async Task LoadAsync()
{
List<ArticulosGlobales> all;
using( MyDbContext db = new MyDbContext( this.connectionString ) )
{
all = await db.ArticulosGlobales
.OrderBy( p => p.codigo )
.ToListAsync()
.ConfigureAwaita(false);
}
this.searchSubsets.Clear();
this.searchSubsets[ String.Empty ] = all;
}
private async void txtBusqueda_TextChanged(object sender, EventArgs e)
{
if( this.searchSubsets.Count == 0 )
{
await this.LoadAsync();
}
String search = ((TextBox)sender).Text;
// Has this search already been performed?
if( this.searchSubsets.TryGetValue( search, out List<ArticulosGlobales> cachedResults ) )
{
dtGridDatos.DataSource = cachedResults;
return;
}
// Is there a previous search we can take a subset of?
for( Int32 i = search.Length - 2; i >= 0; --i )
{
String prefix = search.Substring( 0, i );
if( this.searchSubsets.TryGetValue( prefix, out List<ArticulosGlobales> prefixResults )
{
List<ArticulosGlobales> subset = prefixResults
.Where( p => p.codigo.StartsWith( search, StringComparison.OrdinalIgnoreCase ) )
.ToList();
this.searchSubsets[ search ] = subset;
dtGridDatos.DataSource = cachedResults;
return;
}
}
// Program will never get to this point because the `for` loop above will eventually use an empty-string with `this.searchSubsets.TryGetValue` which *will* have a result, so just throw an exception here instead to keep the C# compiler happy:
throw new InvalidOperationException( "This will never be thrown." );
}
推荐阅读
- r - 如何将公式应用于 R 数据框列的一部分?
- swift - 如何在当前 vc 上获得 largeTitle,但在 vc 上获得小标题?
- android - 无法更改 webview 中元素的位置
- python - 如何将字典字典转换为矩阵?
- azure - 在u-sql中转换数据类型和concat列
- r - 如何将 lavaan 模型的估计器和优化方法作为变量?
- azure-powershell - 重新启动 Azure Web 应用程序
- batch-file - 批量删除除指定文件和目录外的所有文件和目录
- python - Elasticsearch 中唯一值的输出列表
- c# - 如何不合并每个合并事件的 webconfig 文件?