如何快速向DataTable插入大量行并绑定至DataGridView
优化20万条数据插入DataTable并绑定DataGridView的方案
一、先关闭DataTable的事件与约束
DataTable默认会在新增行时触发各类事件(如RowChanged)、检查约束,这对批量插入是额外性能开销。可以先临时禁用,插入完成后恢复:
public static void InsertAutomaticDataRows<T>(this DataTable dataTable, IEnumerable<T> objects) { var properties = typeof(T).GetProperties(); // 保存原始状态 var originalConstraintState = dataTable.EnforceConstraints; dataTable.BeginLoadData(); // 禁用通知、约束检查 try { foreach (var obj in objects) { var row = dataTable.NewRow(); foreach (var property in properties) { row[property.Name] = property.GetValue(obj) ?? DBNull.Value; } dataTable.Rows.Add(row); } } finally { dataTable.EndLoadData(); // 恢复通知与约束 dataTable.EnforceConstraints = originalConstraintState; } }
二、缓存反射委托替代重复GetValue
反射的GetValue调用本身较慢,提前把属性获取逻辑编译成委托缓存起来,避免循环里重复反射开销:
// 全局缓存属性获取委托 private static readonly Dictionary<Type, Dictionary<string, Func<object, object>>> _propertyGetterCache = new(); public static void InsertAutomaticDataRows<T>(this DataTable dataTable, IEnumerable<T> objects) { var type = typeof(T); // 初始化并缓存委托 if (!_propertyGetterCache.TryGetValue(type, out var getters)) { getters = type.GetProperties() .ToDictionary(p => p.Name, p => { var paramExpr = Expression.Parameter(typeof(object)); var castExpr = Expression.Convert(paramExpr, type); var propAccessExpr = Expression.Property(castExpr, p); var resultExpr = Expression.Convert(propAccessExpr, typeof(object)); return Expression.Lambda<Func<object, object>>(resultExpr, paramExpr).Compile(); }); _propertyGetterCache[type] = getters; } var originalConstraintState = dataTable.EnforceConstraints; dataTable.BeginLoadData(); try { foreach (var obj in objects) { var row = dataTable.NewRow(); foreach (var kvp in getters) { row[kvp.Key] = kvp.Value(obj) ?? DBNull.Value; } dataTable.Rows.Add(row); } } finally { dataTable.EndLoadData(); dataTable.EnforceConstraints = originalConstraintState; } }
三、跳过DataTable,直接绑定IEnumerable
如果不需要DataTable的额外功能,直接把数据源绑定到DataGridView能省去转换开销。同时开启虚拟模式优化大数据渲染:
// 开启虚拟模式 dataGridView1.VirtualMode = true; var dataList = objects.ToList(); // 提前枚举避免重复计算 dataGridView1.DataSource = dataList; // 绑定CellValueNeeded事件提供数据 dataGridView1.CellValueNeeded += (sender, e) => { var item = dataList[e.RowIndex]; var property = typeof(T).GetProperties()[e.ColumnIndex]; e.Value = property.GetValue(item) ?? DBNull.Value; };
四、DataGridView绑定前暂停UI更新
绑定数据前先暂停控件布局更新,避免每新增一行就刷新UI:
// 暂停UI更新 dataGridView1.SuspendLayout(); dataGridView1.DataSource = null; // 执行DataTable插入操作 yourDataTable.InsertAutomaticDataRows(yourObjects); // 绑定并恢复UI dataGridView1.DataSource = yourDataTable; dataGridView1.ResumeLayout();
五、用LoadDataRow批量插入
将对象转换为值数组后,使用LoadDataRow方法插入,比逐行Add效率更高:
public static void InsertAutomaticDataRows<T>(this DataTable dataTable, IEnumerable<T> objects) { var properties = typeof(T).GetProperties(); var originalConstraintState = dataTable.EnforceConstraints; dataTable.BeginLoadData(); try { foreach (var obj in objects) { var values = new object[properties.Length]; for (int i = 0; i < properties.Length; i++) { values[i] = properties[i].GetValue(obj) ?? DBNull.Value; } dataTable.LoadDataRow(values, true); } } finally { dataTable.EndLoadData(); dataTable.EnforceConstraints = originalConstraintState; } }
内容的提问来源于stack exchange,提问作者Alireza Noori
相关产品推荐
相关产品推荐

