C# DataGridView加载记录过慢,Dapper操作SQL性能优化咨询
问题根因分析
- 异步写法完全无效:现有
LoadProduct方法将数据库查询逻辑全部包裹在Invoke委托内,所有查询操作仍在UI线程执行,Task.Run仅增加了线程调度开销,没有起到后台加载的作用,和直接绑定数据源的性能表现完全一致。 - SQL查询性能波动:数据缓存命中时(SQL Server将常用数据加载到内存缓冲池、执行计划缓存存在),查询速度很快;如果缓存失效(执行计划被回收、数据从内存中换出),SQL需要重新编译查询、从磁盘读取数据,就会出现10秒以上的延迟,这是你遇到时快时慢的核心原因。
- DataGridView渲染开销高:直接将携带完整图片字节数组的列表绑定到控件,绑定时会一次性将所有图片字节数组解码为可渲染的图像对象,数据量稍大就会阻塞UI线程。
- 属性映射不匹配:
Product类中价格属性名为Price,但SQL查询和插入的字段为ComboPrice,虽然数据库大小写不敏感的情况下可以勉强映射,但会增加Dapper的映射开销,甚至可能出现字段赋值失败的问题。 - 图片读取逻辑存在缺陷:插入图片时仅调用一次
stream.Read,该方法不保证一次性读取全部流内容,极易出现图片存储不完整的问题。
优化方案
1. 修正异步加载逻辑
将数据库查询放到后台线程执行,仅绑定数据源时切回UI线程:
public async void LoadProduct() { // 后台执行查询,不阻塞UI var products = await Task.Run(() => db.GetProduct()); // 仅UI绑定操作在主线程执行 dataGridView1.DataSource = products; }
2. 优化数据库查询性能
- 建立覆盖索引,避免全表扫描,提升查询稳定性:
CREATE NONCLUSTERED INDEX IX_Table_Products_Query ON Table_Products (Name) INCLUDE (Image, ComboPrice);
- 数据量大于50条时增加分页逻辑,每次仅查询当前页需要显示的10-20条数据,大幅降低数据传输和渲染开销。
- 优化Dapper查询配置,开启非缓冲读取降低内存占用,同时统一字段映射:
public List<Product> GetProduct() { // 直接复用静态连接字符串,不要每次新建Connection实例 using (IDbConnection connection = new System.Data.SqlClient.SqlConnection(静态连接字符串)) { // 字段别名保证和Product属性名匹配,关闭缓冲降低内存开销 return connection.Query<Product>("SELECT Name, Image, ComboPrice AS Price FROM Table_Products", buffered: false).ToList(); } }
3. 优化插入逻辑
- 修复图片读取不全的问题,直接用内存流拷贝全量数据:
private void Insert_Click(object sender, EventArgs e) { byte[] photo; using (var stream = File.OpenRead(txtPhoto.Text)) using (var ms = new MemoryStream()) { stream.CopyTo(ms); photo = ms.ToArray(); } db.InsertProduct(txt_description.Text, photo, Convert.ToDecimal(txt_price.Text)); }
- 简化插入代码,不需要额外创建List对象:
public void InsertProduct(string name, byte[] image, decimal price) { using (IDbConnection connection = new System.Data.SqlClient.SqlConnection(静态连接字符串)) { connection.Execute( "INSERT INTO Table_Products(Name, Image, ComboPrice) VALUES(@Name, @Image, @ComboPrice)", new { Name = name, Image = image, ComboPrice = price } ); } }
4. 优化DataGridView渲染
- 开启DataGridView的双缓冲、虚拟模式,仅渲染当前可见行的内容。
- 优先将图片存储到本地磁盘,数据库仅存储图片路径,查询时仅拉取文本数据,需要显示图片时再异步加载对应路径的文件,可将查询速度提升90%以上。
内容的提问来源于stack exchange,提问作者Aaric Aaiden
相关产品推荐
相关产品推荐

