1. C#数据库编程基础与环境准备
在.NET生态系统中,C#作为主力开发语言,其数据库访问能力经历了ADO.NET到Entity Framework的演进。我们先从最基础的连接字符串配置开始:
csharp复制// SQL Server连接字符串示例
string connectionString = "Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;";
// 使用SqlConnection建立连接
using (SqlConnection connection = new SqlConnection(connectionString))
{
try
{
connection.Open();
Console.WriteLine("连接成功!");
// 后续数据库操作...
}
catch (SqlException ex)
{
Console.WriteLine($"连接失败:{ex.Message}");
}
}
关键组件说明:
System.Data.SqlClient:SQL Server专用提供程序Microsoft.Data.SqlClient:新一代SQL Server客户端(推荐)System.Data.Odbc:通用ODBC连接方案System.Data.OleDb:传统OLE DB连接方式
安全提示:永远不要将明文密码硬编码在代码中!应使用ConfigurationManager或环境变量存储敏感信息。
2. ADO.NET核心操作模式详解
2.1 基础CRUD操作
查询数据示例:
csharp复制string sql = "SELECT * FROM Products WHERE Price > @minPrice";
using (SqlCommand command = new SqlCommand(sql, connection))
{
command.Parameters.AddWithValue("@minPrice", 50.0);
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine($"产品:{reader["Name"]}, 价格:{reader["Price"]}");
}
}
}
事务处理模式:
csharp复制using (SqlTransaction transaction = connection.BeginTransaction())
{
try
{
// 执行多个SQL命令
new SqlCommand("UPDATE Account SET Balance = Balance - 100 WHERE Id = 1",
connection, transaction).ExecuteNonQuery();
new SqlCommand("UPDATE Account SET Balance = Balance + 100 WHERE Id = 2",
connection, transaction).ExecuteNonQuery();
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
2.2 参数化查询最佳实践
避免SQL注入的黄金法则:
csharp复制// 错误示范(存在注入风险)
string unsafeSql = $"SELECT * FROM Users WHERE Name = '{userInput}'";
// 正确做法
string safeSql = "SELECT * FROM Users WHERE Name = @userName";
command.Parameters.Add("@userName", SqlDbType.NVarChar, 50).Value = userInput;
参数类型映射表:
| .NET类型 | SqlDbType | 大小限制 |
|---|---|---|
| string | NVarChar | 指定MaxLength |
| int | Int | - |
| decimal | Decimal | 指定精度 |
| DateTime | DateTime2 | - |
| byte[] | VarBinary | 指定Size |
3. Entity Framework Core实战
3.1 数据建模与迁移
定义数据模型:
csharp复制public class Product
{
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
[Column(TypeName = "decimal(18,2)")]
public decimal Price { get; set; }
public DateTime CreatedDate { get; set; } = DateTime.UtcNow;
}
public class AppDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer(Configuration.GetConnectionString("Default"));
}
执行迁移命令:
bash复制dotnet ef migrations add InitialCreate
dotnet ef database update
3.2 高效查询技巧
LINQ查询示例:
csharp复制var expensiveProducts = await context.Products
.Where(p => p.Price > 100)
.OrderByDescending(p => p.Price)
.Select(p => new { p.Name, p.Price })
.ToListAsync();
性能优化要点:
- 始终使用
AsNoTracking()处理只读查询 - 避免
ToList()过早执行查询 - 使用
Include和ThenInclude处理关联数据 - 对于复杂查询考虑使用原始SQL
csharp复制// 原始SQL查询示例
var products = context.Products
.FromSqlRaw("EXEC GetProductsByPriceRange @MinPrice, @MaxPrice",
new SqlParameter("@MinPrice", 50),
new SqlParameter("@MaxPrice", 200))
.AsNoTracking()
.ToList();
4. 数据库安全深度防护
4.1 连接安全配置
加密连接配置:
csharp复制// 在连接字符串中添加加密选项
string secureConnection = "Server=myserver;Database=mydb;Encrypt=True;TrustServerCertificate=False;";
连接池安全设置:
properties复制Max Pool Size=100 // 最大连接数
Min Pool Size=5 // 最小保持连接数
Connection Timeout=30 // 超时秒数
4.2 权限最小化原则
数据库用户权限矩阵:
| 操作类型 | 所需权限 | 推荐角色 |
|---|---|---|
| 只读查询 | SELECT | db_datareader |
| 数据修改 | INSERT/UPDATE/DELETE | db_datawriter |
| 架构变更 | ALTER/CREATE | db_ddladmin |
| 完全控制 | CONTROL | db_owner |
4.3 审计与监控实现
SQL Server审计示例:
sql复制-- 创建服务器级审计
CREATE SERVER AUDIT MyAppAudit
TO FILE (FILEPATH = 'C:\Audits\')
WITH (QUEUE_DELAY = 1000);
-- 创建数据库审计规范
CREATE DATABASE AUDIT SPECIFICATION MyAppDBSpec
FOR SERVER AUDIT MyAppAudit
ADD (SELECT, INSERT, UPDATE, DELETE ON SCHEMA::dbo BY public);
C#中的操作审计:
csharp复制public override int SaveChanges()
{
var entries = ChangeTracker.Entries()
.Where(e => e.State == EntityState.Added ||
e.State == EntityState.Modified);
foreach (var entry in entries)
{
AuditLog.LogChange(entry.Entity.GetType().Name,
entry.State.ToString(),
JsonSerializer.Serialize(entry.CurrentValues));
}
return base.SaveChanges();
}
5. 高级主题与性能优化
5.1 批量操作策略
Entity Framework批量插入:
csharp复制// 使用EF Core的批量扩展
context.BulkInsert(products, options => {
options.BatchSize = 1000;
options.InsertIfNotExists = true;
});
临时表优化方案:
csharp复制// 使用表变量批量处理
context.Database.ExecuteSqlRaw(@"
DECLARE @TempProducts TABLE (Id INT, Name NVARCHAR(100));
INSERT INTO @TempProducts VALUES {string.Join(",", values)};
MERGE Products AS target
USING @TempProducts AS source
ON target.Id = source.Id
WHEN MATCHED THEN UPDATE SET target.Name = source.Name
WHEN NOT MATCHED THEN INSERT (Id, Name) VALUES (source.Id, source.Name);");
5.2 连接弹性与重试策略
配置Polly策略:
csharp复制services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("Default"),
sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 5,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
}));
自定义重试逻辑:
csharp复制var policy = Policy
.Handle<SqlException>(ex => ex.IsTransient)
.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(3),
TimeSpan.FromSeconds(5)
});
await policy.ExecuteAsync(async () => {
using var connection = new SqlConnection(connectionString);
await connection.OpenAsync();
// 执行数据库操作
});
6. 实际项目中的经验总结
连接泄露检测技巧:
csharp复制// 在开发环境添加连接追踪
#if DEBUG
connection.StateChange += (s, e) =>
Debug.WriteLine($"连接状态变化:{e.OriginalState} -> {e.CurrentState}");
#endif
性能监控指标:
| 指标名称 | 健康阈值 | 监控方式 |
|---|---|---|
| 连接池等待时间 | < 500ms | 性能计数器 |
| 查询响应时间 | < 1s (简单查询) | Application Insights |
| 事务持续时间 | < 2s | 自定义日志 |
| 并发连接数 | < 最大连接数的80% | SQL Server DMV |
缓存策略建议:
- 对于静态参考数据使用内存缓存
- 实现二级缓存(Redis + 本地缓存)
- 对查询结果进行哈希签名缓存
- 设置合理的过期策略
csharp复制// 缓存实现示例
public async Task<List<Product>> GetProducts(bool useCache = true)
{
const string cacheKey = "all_products";
if(useCache && _memoryCache.TryGetValue(cacheKey, out List<Product> cached))
return cached;
var products = await _context.Products.AsNoTracking().ToListAsync();
_memoryCache.Set(cacheKey, products, new MemoryCacheEntryOptions {
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30)
});
return products;
}
在长期项目维护中,我发现这些实践特别有价值:
- 始终为数据库操作编写单元测试
- 使用Docker容器保持开发与生产环境一致
- 定期进行SQL查询计划分析
- 建立数据库变更的版本控制流程
