C#中使用Dapper结合仓储模式查询性能过慢的优化咨询
问题描述
我有一个用于获取供应商相关信息(如零件、发票)的小型测试项目,尝试使用仓储模式实现,但不确定用法是否正确。功能正常但性能极慢。
我定义了多个实体类,例如:
public class SupplierPart { public int PartId { get; set; } public string PartNumber { get; set; } public string PartName { get; set; } // ...其他属性 }
每个实体类对应创建如下接口:
public interface ISupplierPartRepository { IEnumerable<SupplierPart> GetAllSupplierParts(); IEnumerable<SupplierPart> GetAllSupplierPartsOfSupplier(int SupplierId); void UpdateSupplierPart(SupplierPart part); }
并实现对应的仓储类:
public class SupplierPartRepository : ISupplierPartRepository { public IEnumerable<SupplierPart> GetAllSupplierParts() { using (IDbConnection db = new MySqlConnection(AppConnection.ConnectionString)) { string q = @"SELECT ..."; return db.Query<SupplierPart>(q, commandType: CommandType.Text); } } public IEnumerable<SupplierPart> GetAllSupplierPartsOfSupplier(int SupplierId) { using (IDbConnection db = new MySqlConnection(AppConnection.ConnectionString)) { string q = @"SELECT ..."; return db.Query<SupplierPart>(q, new { SupplierId }); } } public void UpdateSupplierPart(SupplierPart part) { using (IDbConnection db = new MySqlConnection(AppConnection.ConnectionString)) { // ...更新逻辑 } } }
在窗体中我这样调用:
dataGridView1.DataSource = supplierPartRepository.GetAllSupplierParts().ToList(); Code_Actions.FillComboBox(combobox2, productGroupRepository.GetAllProductGroups(), "GroupName", "GroupId"); Code_Actions.FillComboBox(combobox3, partRepository.GetAllParts(), "Number", "Number"); Code_Actions.FillComboBox(combobox4, unitRepository.GetAllUnits(), "UnitText", "UnitId"); // ...其他调用
当同时调用多个仓储方法时性能极慢,但仅调用填充DataGridView的代码时速度很快。请问该如何优化性能?
优化方案
1. 复用数据库连接,减少连接开销
当前每个仓储方法都独立创建新的数据库连接,多次调用会频繁执行连接建立/断开操作,这是性能瓶颈的核心原因。可以通过工作单元模式统一管理连接:
- 定义工作单元接口与实现,在构造时创建一次连接,所有仓储共享该连接:
public interface IUnitOfWork : IDisposable { ISupplierPartRepository SupplierParts { get; } IProductGroupRepository ProductGroups { get; } IPartRepository Parts { get; } IUnitRepository Units { get; } // 其他仓储接口 } public class UnitOfWork : IUnitOfWork { private readonly IDbConnection _dbConnection; private ISupplierPartRepository _supplierPartRepo; private IProductGroupRepository _productGroupRepo; // ...其他仓储字段 public UnitOfWork() { _dbConnection = new MySqlConnection(AppConnection.ConnectionString); _dbConnection.Open(); // 提前打开连接,避免延迟 } public ISupplierPartRepository SupplierParts { get { if (_supplierPartRepo == null) _supplierPartRepo = new SupplierPartRepository(_dbConnection); return _supplierPartRepo; } } // 其他仓储属性同理,注入共享连接 public void Dispose() { _dbConnection?.Close(); _dbConnection?.Dispose(); } }
- 修改仓储类,通过构造函数接收外部连接,不再自行创建:
public class SupplierPartRepository : ISupplierPartRepository { private readonly IDbConnection _dbConnection; public SupplierPartRepository(IDbConnection dbConnection) { _dbConnection = dbConnection; } public IEnumerable<SupplierPart> GetAllSupplierParts() { string q = @"SELECT ..."; return _dbConnection.Query<SupplierPart>(q, commandType: CommandType.Text); } // 其他方法直接使用_dbConnection,无需using包裹 }
- 窗体调用时,通过工作单元统一获取仓储,共享连接:
using (var uow = new UnitOfWork()) { dataGridView1.DataSource = uow.SupplierParts.GetAllSupplierParts().ToList(); Code_Actions.FillComboBox(combobox2, uow.ProductGroups.GetAllProductGroups(), "GroupName", "GroupId"); Code_Actions.FillComboBox(combobox3, uow.Parts.GetAllParts(), "Number", "Number"); Code_Actions.FillComboBox(combobox4, uow.Units.GetAllUnits(), "UnitText", "UnitId"); }
2. 批量加载数据,减少数据库查询次数
如果多个下拉框的基础数据无关联冲突,可以将多个查询合并为一次多结果集查询,减少数据库交互次数:
// 在工作单元中添加批量获取方法 public (IEnumerable<ProductGroup> ProductGroups, IEnumerable<Part> Parts, IEnumerable<Unit> Units) GetAllLookupData() { using (var multi = _dbConnection.QueryMultiple(@" SELECT * FROM ProductGroups; SELECT * FROM Parts; SELECT * FROM Units; ")) { var productGroups = multi.Read<ProductGroup>(); var parts = multi.Read<Part>(); var units = multi.Read<Unit>(); return (productGroups, parts, units); } }
调用时一次获取所有数据,再填充下拉框:
var lookupData = uow.GetAllLookupData(); Code_Actions.FillComboBox(combobox2, lookupData.ProductGroups, "GroupName", "GroupId"); Code_Actions.FillComboBox(combobox3, lookupData.Parts, "Number", "Number"); Code_Actions.FillComboBox(combobox4, lookupData.Units, "UnitText", "UnitId");
3. 优化查询语句与索引
- 避免使用
SELECT *,只查询业务需要的字段,减少数据传输量; - 为查询中频繁使用的条件字段(如
SupplierParts.SupplierId、各基础表的主键)添加数据库索引,加快查询速度。
4. 异步加载提升UI响应
如果数据量较大,可将仓储方法改为异步版本,避免UI线程阻塞,提升用户感知性能:
- 修改仓储方法为异步:
public async Task<IEnumerable<SupplierPart>> GetAllSupplierPartsAsync() { string q = @"SELECT PartId, PartNumber, PartName FROM SupplierParts"; return await _dbConnection.QueryAsync<SupplierPart>(q); }
- 窗体调用时使用
await:
private async void Form_Load(object sender, EventArgs e) { using (var uow = new UnitOfWork()) { dataGridView1.DataSource = await uow.SupplierParts.GetAllSupplierPartsAsync(); var lookupData = await uow.GetAllLookupDataAsync(); // 填充下拉框 } }
内容的提问来源于stack exchange,提问作者Lotte
相关产品推荐
相关产品推荐

