1. 项目概述
在.NET生态中与MySQL数据库建立连接是许多开发者日常工作中不可或缺的基础技能。不同于SQL Server与.NET平台的原生兼容性,MySQL作为开源数据库的代表,在C#项目中的集成需要更多技术细节的考量。本文将深入剖析ADO.NET、Entity Framework Core以及Dapper三种主流方案,从基础连接字符串配置到高级ORM映射,全面覆盖实际开发中的各类场景。
2. 环境准备与基础配置
2.1 必要组件安装
首先需要通过NuGet获取MySQL官方提供的连接驱动:
Install-Package MySql.Data Install-Package MySql.Data.EntityFramework对于Entity Framework Core方案需额外安装:
Install-Package Pomelo.EntityFrameworkCore.MySql注意:Oracle官方提供的MySQL Connector/NET与社区驱动的Pomelo.EntityFrameworkCore.MySql在功能支持上存在差异,生产环境建议进行性能测试后再做选择。
2.2 连接字符串详解
基础连接字符串包含以下关键参数:
"Server=127.0.0.1;Port=3306;Database=mydb;Uid=root;Pwd=123456;"高级配置项示例:
"Server=localhost;Database=test;Uid=appuser;Pwd=securePwd123; ConnectionTimeout=30;Pooling=true;MinPoolSize=5;MaxPoolSize=100; CharSet=utf8mb4;AllowPublicKeyRetrieval=true;"- Pooling:连接池默认开启,建议根据并发量调整池大小
- CharSet:必须明确指定为utf8mb4以支持完整Unicode字符集
- AllowPublicKeyRetrieval:SSL连接时的特殊配置项
3. 核心连接方案实现
3.1 ADO.NET原生方式
基础连接示例:
using MySql.Data.MySqlClient; string connStr = "server=localhost;user=root;database=test;port=3306;password=123456"; using (MySqlConnection conn = new MySqlConnection(connStr)) { try { conn.Open(); string sql = "SELECT * FROM users WHERE id=@id"; MySqlCommand cmd = new MySqlCommand(sql, conn); cmd.Parameters.AddWithValue("@id", 1); using (MySqlDataReader rdr = cmd.ExecuteReader()) { while (rdr.Read()) { Console.WriteLine(rdr["username"]); } } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } }事务处理模板:
using (MySqlTransaction trans = conn.BeginTransaction()) { try { // 执行多个命令 trans.Commit(); } catch { trans.Rollback(); throw; } }3.2 Entity Framework Core集成
DbContext配置示例:
services.AddDbContext<AppDbContext>(options => options.UseMySql( Configuration.GetConnectionString("Default"), new MySqlServerVersion(new Version(8, 0, 26)) ));实体类映射示例:
[Table("products")] public class Product { [Key] [Column("product_id")] public int Id { get; set; } [Required] [MaxLength(100)] [Column("product_name")] public string Name { get; set; } [Column(TypeName = "DECIMAL(10,2)")] public decimal Price { get; set; } }3.3 Dapper轻量级方案
基础查询示例:
using Dapper; var users = conn.Query<User>("SELECT * FROM users WHERE status = @status", new { status = 1 });多表查询处理:
var sql = @"SELECT p.*, c.Name as CategoryName FROM products p JOIN categories c ON p.CategoryId = c.Id"; var products = conn.Query<Product, Category, Product>(sql, (product, category) => { product.Category = category; return product; }, splitOn: "CategoryName");4. 性能优化策略
4.1 连接池配置建议
| 参数 | 默认值 | 生产建议 | 说明 |
|---|---|---|---|
| Pooling | true | true | 必须开启 |
| MinPoolSize | 0 | 5-10 | 预热连接数 |
| MaxPoolSize | 100 | 100-200 | 根据服务器配置调整 |
| ConnectionTimeout | 15 | 30 | 超时秒数 |
| ConnectionLifeTime | 0 | 300 | 连接回收时间(秒) |
4.2 批量操作优化
使用MySqlBulkLoader实现高效导入:
var bulk = new MySqlBulkLoader(conn) { TableName = "large_data", FieldTerminator = ",", LineTerminator = "\n", FileName = "data.csv" }; bulk.Load();EF Core批量插入扩展:
context.BulkInsert(entities, options => { options.BatchSize = 1000; options.InsertIfNotExists = true; });5. 安全防护措施
5.1 凭证管理方案
推荐采用Azure Key Vault或AWS Secrets Manager动态获取凭据:
var secretClient = new SecretClient( new Uri(keyVaultUrl), new DefaultAzureCredential()); string connStr = await secretClient .GetSecretAsync("mysql-conn-str");5.2 SQL注入防护
参数化查询的三种正确姿势:
// ADO.NET方式 cmd.Parameters.AddWithValue("@param", value); // Dapper方式 conn.Query("SELECT * FROM table WHERE id=@id", new { id }); // EF Core方式 context.Users.FromSqlInterpolated($"SELECT * FROM users WHERE id={id}");6. 异常处理与调试
6.1 常见错误代码处理
| 错误代码 | 含义 | 处理方案 |
|---|---|---|
| 1045 | 访问拒绝 | 检查凭据和主机权限 |
| 2002 | 连接超时 | 验证网络和防火墙设置 |
| 1213 | 死锁 | 重试事务或优化查询 |
| 1062 | 键重复 | 实现upsert逻辑 |
6.2 诊断日志配置
在appsettings.json中配置详细日志:
{ "Logging": { "MySql": { "LogLevel": { "MySql.Data": "Debug", "Microsoft.EntityFrameworkCore.Database.Command": "Information" } } } }7. 高级应用场景
7.1 JSON类型支持
MySQL 8.0+的JSON操作示例:
// 查询JSON字段 var result = conn.QuerySingle<string>( "SELECT JSON_EXTRACT(profile, '$.address') FROM users WHERE id=1"); // EF Core更新JSON字段 context.Users .Where(u => u.Id == 1) .ExecuteUpdate(u => u .SetProperty(p => p.ProfileJson, p => "JSON_SET(p.ProfileJson, '$.phone', '123456')"));7.2 分布式事务处理
使用TransactionScope实现跨库事务:
using (var scope = new TransactionScope( TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted })) { // 操作多个数据库 scope.Complete(); }8. 架构设计建议
8.1 分层设计示例
推荐的项目结构:
- MyProject.Data ├── Repositories (具体数据操作) ├── Entities (领域模型) └── DbContexts (数据库上下文) - MyProject.Services (业务逻辑层) - MyProject.Api (表现层)8.2 读写分离实现
基于代理模式的实现方案:
public class DbConnectionFactory { private readonly string _writeConnStr; private readonly string _readConnStr; public IDbConnection CreateWriteConnection() => new MySqlConnection(_writeConnStr); public IDbConnection CreateReadConnection() => new MySqlConnection(_readConnStr); }9. 迁移与部署
9.1 数据库变更管理
EF Core迁移命令示例:
# 创建迁移 dotnet ef migrations add InitialCreate # 应用迁移 dotnet ef database update # 生成SQL脚本 dotnet ef migrations script --output migrations.sql9.2 Docker部署配置
docker-compose.yml示例:
version: '3' services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: securepassword MYSQL_DATABASE: appdb ports: - "3306:3306" volumes: - mysql_data:/var/lib/mysql volumes: mysql_data:10. 实际案例解析
电商项目中的典型数据访问模式:
// 商品分页查询 public async Task<PageResult<Product>> SearchProducts(ProductQuery query) { var baseQuery = context.Products .Where(p => p.CategoryId == query.CategoryId); if (!string.IsNullOrEmpty(query.Keyword)) { baseQuery = baseQuery.Where(p => EF.Functions.Like(p.Name, $"%{query.Keyword}%")); } var total = await baseQuery.CountAsync(); var items = await baseQuery .OrderBy(p => p.Price) .Skip((query.Page - 1) * query.PageSize) .Take(query.PageSize) .ToListAsync(); return new PageResult<Product>(items, total); }在长期维护企业级.NET应用的过程中,我发现MySQL连接稳定性很大程度上取决于连接池参数的合理配置。特别是在微服务架构下,建议为每个服务实例设置独立的连接池前缀(通过在连接字符串中添加ApplicationName参数),这样可以更精确地监控和管理各个服务的数据库连接状态。