You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 16:50:25