1. 技术栈选择与准备
我们选用C# 10.0 + MySqlConnector 2.3.8 + MySQL 8.0组合。MySqlConnector相比官方MySQL Connector/NET有更活跃的维护,支持async/await特性,且性能提升约30%(根据官方基准测试)。通过NuGet安装:
Install-Package MySqlConnector -Version 2.3.8
2. 基础调用流程(带完整示例)
2.1 创建测试存储过程
先创建一个包含输入/输出参数和结果集的存储过程:
DELIMITER $$
CREATE PROCEDURE GetEmployeeDetails(
IN deptId INT,
OUT totalCount INT,
INOUT bonusRate DECIMAL(5,2)
)
BEGIN
-- 查询结果集
SELECT * FROM employees WHERE department_id = deptId;
-- 设置输出参数
SELECT COUNT(*) INTO totalCount FROM employees;
-- 修改INOUT参数
SET bonusRate = bonusRate * 1.1;
END$$
DELIMITER ;
2.2 C#调用实现
完整控制台应用示例:
using MySqlConnector;
using System.Data;
public class Program
{
const string connStr = "Server=localhost;Database=testdb;User=root;Password=123456;";
public static async Task Main()
{
await using var connection = new MySqlConnection(connStr);
await connection.OpenAsync();
// 创建命令对象(重点:必须指定CommandType)
await using var cmd = new MySqlCommand("GetEmployeeDetails", connection)
{
CommandType = CommandType.StoredProcedure
};
// 输入参数
cmd.Parameters.Add(new MySqlParameter("deptId", 5));
// 输出参数(必须指定方向)
cmd.Parameters.Add(new MySqlParameter("totalCount", MySqlDbType.Int32)
{
Direction = ParameterDirection.Output
});
// INOUT参数(初始值+方向)
cmd.Parameters.Add(new MySqlParameter("bonusRate", MySqlDbType.Decimal)
{
Value = 1.5m,
Direction = ParameterDirection.InputOutput,
Precision = 5,
Scale = 2
});
// 执行并读取结果集
await using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
Console.WriteLine($"员工ID: {reader["id"]}, 姓名: {reader["name"]}");
}
// 必须显式关闭Reader才能获取输出参数
await reader.CloseAsync();
// 获取参数值(注意类型转换)
int total = (int)cmd.Parameters["totalCount"].Value;
decimal rate = (decimal)cmd.Parameters["bonusRate"].Value;
Console.WriteLine($"总人数: {total}, 调整后奖金系数: {rate}");
}
}
3. 高级应用场景
3.1 事务处理
存储过程与事务的完美组合:
await using var transaction = await connection.BeginTransactionAsync();
try
{
cmd.Transaction = transaction;
// 执行多个存储过程...
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
3.2 批量参数处理
使用MySqlBatch提升批量执行效率:
var batch = new MySqlBatch(connection)
{
BatchCommands =
{
new MySqlBatchCommand("Call UpdateInventory(@productId, @qty)")
{
Parameters =
{
new MySqlParameter("@productId", 101),
new MySqlParameter("@qty", 10)
}
},
new MySqlBatchCommand("Call LogOperation(@userId, @action)")
{
Parameters =
{
new MySqlParameter("@userId", "admin"),
new MySqlParameter("@action", "库存更新")
}
}
}
};
await batch.ExecuteNonQueryAsync();
4. 技术优缺点分析
4.1 优势亮点
- 性能卓越:二进制协议传输比普通SQL快约40%
- 参数安全:天然防止SQL注入
- 复用性强:统一业务逻辑入口
- 事务简化:复杂操作原子性保证
4.2 潜在局限
- 调试复杂度:需要跨语言调试
- 版本管理:需同步维护SQL脚本
- 移植成本:不同数据库语法差异
5. 关键注意事项
5.1 参数顺序陷阱
MySQL存储过程参数必须按照创建顺序添加,否则会引发错误。建议使用参数名称显式指定:
// 正确做法(指定参数名称)
cmd.Parameters.Add(new MySqlParameter("deptId", MySqlDbType.Int32)
{
Value = 5,
Direction = ParameterDirection.Input
});
5.2 类型映射要点
常见类型对应关系:
MySQL类型 | C#类型 | MySqlDbType枚举 |
---|---|---|
INT | int | Int32 |
DECIMAL(10,2) | decimal | Decimal |
VARCHAR(255) | string | VarChar |
DATETIME | DateTime | DateTime |
BLOB | byte[] | Blob |
5.3 连接池优化
在连接字符串中配置:
Pooling=true;
MinimumPoolSize=5;
MaximumPoolSize=100;
ConnectionIdleTimeout=300;
6. 性能优化实践
6.1 预处理命令
使用Prepare提升重复执行效率:
if (!cmd.IsPrepared)
{
await cmd.PrepareAsync();
}
// 循环执行已预处理的命令...
6.2 异步编程
充分利用async/await避免线程阻塞:
public async Task<decimal> CalculateSalaryAsync(int userId)
{
await using var cmd = new MySqlCommand("CalcSalary", connection)
{
CommandType = CommandType.StoredProcedure
};
cmd.Parameters.AddWithValue("userId", userId);
return (decimal)await cmd.ExecuteScalarAsync();
}
7. 典型错误排查
7.1 参数方向缺失
未设置ParameterDirection导致输出参数获取失败:
// 错误示例:缺少Direction设置
cmd.Parameters.Add(new MySqlParameter("totalCount", MySqlDbType.Int32));
// 正确做法
cmd.Parameters.Add(new MySqlParameter("totalCount", MySqlDbType.Int32)
{
Direction = ParameterDirection.Output
});
7.2 结果集未关闭
过早访问输出参数导致异常:
// 错误顺序
var total = cmd.Parameters["totalCount"].Value; // 此处会抛出异常
await reader.CloseAsync();
// 正确流程
await reader.CloseAsync();
var total = cmd.Parameters["totalCount"].Value;
8. 总结与选择建议
在以下场景优先选用存储过程:
- 复杂计算逻辑(如财务核算)
- 高频批量操作(如库存扣减)
- 敏感数据操作(如权限分级)
对于简单CRUD操作,建议直接使用Dapper等轻量级ORM。建议将存储过程版本纳入CI/CD流程管理,使用Flyway等数据库迁移工具保持版本一致性。